@xano/developer-mcp 1.0.57 → 1.0.59

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 +9 -43
  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 +0 -2
  13. package/dist/xanoscript_docs/database.md +3 -7
  14. package/dist/xanoscript_docs/debugging.md +1 -264
  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 +3 -7
  19. package/dist/xanoscript_docs/mcp-servers.md +1 -2
  20. package/dist/xanoscript_docs/middleware.md +1 -3
  21. package/dist/xanoscript_docs/performance.md +8 -198
  22. package/dist/xanoscript_docs/realtime.md +11 -161
  23. package/dist/xanoscript_docs/run.md +2 -184
  24. package/dist/xanoscript_docs/schema.md +1 -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 +0 -2
  38. package/package.json +2 -2
@@ -0,0 +1,161 @@
1
+ ---
2
+ applyTo: "**/*.xs"
3
+ ---
4
+
5
+ # XanoScript Survival Kit
6
+
7
+ > Minimal reference for writing valid XanoScript. For more: `xanoscript_docs({ topic: "<topic>" })`.
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
+ ### Syntax Traps
44
+
45
+ **Trap 1: `elseif` not `else if`**
46
+ ```xs
47
+ // WRONG: conditional { if (...) { } else if (...) { } }
48
+ // RIGHT:
49
+ conditional {
50
+ if ($x > 0) { var $s { value = "positive" } }
51
+ elseif ($x == 0) { var $s { value = "zero" } }
52
+ else { var $s { value = "negative" } }
53
+ }
54
+ ```
55
+
56
+ **Trap 2: Parentheses around filters in expressions**
57
+ ```xs
58
+ // WRONG: if ($arr|count > 0) { }
59
+ // RIGHT:
60
+ if (($arr|count) > 0) { }
61
+
62
+ // WRONG: var $msg { value = $val|to_text ~ " items" }
63
+ // RIGHT:
64
+ var $msg { value = ($val|to_text) ~ " items" }
65
+ ```
66
+
67
+ **Trap 3: Object literals use `:` — block properties use `=`**
68
+ ```xs
69
+ // Object literal (data) — uses : with commas
70
+ var $data { value = { name: "Alice", age: 30 } }
71
+
72
+ // Block property (config) — uses = with NO commas, separate lines
73
+ precondition ($data != null) {
74
+ error_type = "notfound"
75
+ error = "Not found"
76
+ }
77
+ ```
78
+
79
+ **Trap 4: `params` not `body` for api.request**
80
+ ```xs
81
+ // WRONG: api.request { url = "..." method = "POST" body = $payload }
82
+ // RIGHT:
83
+ api.request { url = "..." method = "POST" params = $payload } as $result
84
+ ```
85
+
86
+ **Trap 5: Wrong type names**
87
+ ```xs
88
+ // WRONG: input { boolean active integer count string name }
89
+ // RIGHT:
90
+ input { bool active int count text name }
91
+ ```
92
+
93
+ ### Canonical Function Example
94
+
95
+ ```xs
96
+ function "get_user_greeting" {
97
+ input {
98
+ int user_id
99
+ text? greeting?="Hello"
100
+ }
101
+ stack {
102
+ db.get "user" {
103
+ field_name = "id"
104
+ field_value = $input.user_id
105
+ } as $user
106
+
107
+ precondition ($user != null) {
108
+ error_type = "notfound"
109
+ error = "User not found"
110
+ }
111
+
112
+ var $message { value = ($input.greeting ?? "Hello") ~ ", " ~ $user.name ~ "!" }
113
+ }
114
+ response = $message
115
+ }
116
+ ```
117
+
118
+ ### Canonical API Endpoint Example
119
+
120
+ ```xs
121
+ query "users/{user_id}" verb=GET {
122
+ api_group = "users"
123
+ auth = "user"
124
+ input { int user_id }
125
+ stack {
126
+ db.get "user" { field_name = "id" field_value = $input.user_id } as $user
127
+ precondition ($user != null) { error_type = "notfound" error = "User not found" }
128
+ }
129
+ response = $user
130
+ }
131
+ ```
132
+
133
+ ### Common Operations
134
+
135
+ ```xs
136
+ db.get "table" { field_name = "id" field_value = $id } as $record
137
+ db.query "table" { where = $db.table.active == true } as $records
138
+ db.add "table" { data = { field: value } } as $new
139
+ db.edit "table" { field_name = "id" field_value = $id data = { field: val } }
140
+ db.del "table" { field_name = "id" field_value = $id }
141
+ api.request { url = $url method = "POST" params = $data headers = ["Authorization: Bearer " ~ $env.KEY] } as $res
142
+ function.run "my_func" { input = { param: value } } as $result
143
+ foreach ($items) { each as $item { debug.log { value = $item } } }
144
+ ```
145
+
146
+ ### Input Modifiers
147
+
148
+ ```xs
149
+ input {
150
+ text name // required, not nullable
151
+ text? bio // required, nullable (can send null)
152
+ text role?="user" // optional with default
153
+ text? note? // optional and nullable
154
+ email contact filters=trim // with validation filter
155
+ text[] tags // array type
156
+ }
157
+ ```
158
+
159
+ ### Available Topics
160
+
161
+ essentials, syntax, types, database, functions, apis, tables, tasks, triggers, agents, tools, mcp-servers, security, performance, debugging, unit-testing, workflow-tests, middleware, addons, realtime, streaming, schema, integrations, workspace, branch, run, frontend
@@ -148,12 +148,6 @@ if (($arr|count) == 0) { ... }
148
148
  var $message {
149
149
  value = ($status|to_text) ~ ": " ~ ($data|json_encode)
150
150
  }
151
-
152
- // ✅ Correct — filter result used in arithmetic
153
- var $total { value = ($prices|sum) + $tax }
154
-
155
- // ✅ Correct — filter result compared
156
- var $is_long { value = ($text|strlen) > 100 }
157
151
  ```
158
152
 
159
153
  **Summary:** Any time you apply an operator (`>`, `<`, `==`, `!=`, `~`, `+`, `-`, etc.) to a filtered value, wrap the `$var|filter` portion in its own parentheses.
@@ -319,11 +319,9 @@ table "order" {
319
319
 
320
320
  ## Best Practices
321
321
 
322
- 1. **Always define `id`** - Every table needs a primary key named `id`
323
- 2. **Use `auth = true`** only for authentication tables (typically just `user`)
324
- 3. **Add indexes** for fields used in WHERE clauses and JOINs
325
- 4. **Use appropriate types** - `email` for emails, `password` for credentials
326
- 5. **Default timestamps** - Use `?=now` for created_at fields
322
+ 1. **Always define `int id`** - Every table requires a primary key field named `id`
323
+ 2. **Use `auth = true`** only for the authentication table (typically just `user`)
324
+ 3. **Add indexes** for fields used in `db.query` WHERE clauses and JOINs to avoid full table scans
327
325
 
328
326
  ---
329
327
 
@@ -254,9 +254,7 @@ task "risky_sync" {
254
254
 
255
255
  1. **Descriptive names** - Indicate what and when: `daily_cleanup`, `hourly_sync`
256
256
  2. **Handle errors** - Use try_catch for external dependencies
257
- 3. **Consider timezone** - Schedule uses UTC (+0000)
258
- 4. **Batch operations** - Process in chunks for large datasets
259
- 5. **Set end dates** - Use ends_on for temporary schedules
257
+ 3. **Consider timezone** - Schedule uses UTC (+0000); use `ends_on` for temporary schedules
260
258
 
261
259
  ---
262
260
 
@@ -298,9 +298,7 @@ tool "cancel_order" {
298
298
 
299
299
  1. **Write clear instructions** - This is what the AI reads to understand the tool
300
300
  2. **Describe all inputs** - Help AI construct valid requests
301
- 3. **Use enums for fixed options** - Reduces AI errors
302
- 4. **Keep tools focused** - One task per tool
303
- 5. **Limit response size** - Don't return huge datasets
301
+ 3. **Use enums for fixed options** - Reduces AI errors; keep tools focused to one task
304
302
 
305
303
  ---
306
304
 
@@ -589,77 +589,11 @@ mcp_server_trigger "database_tool_handler" {
589
589
 
590
590
  ---
591
591
 
592
- ## Common Patterns
593
-
594
- ### Error Handling in Triggers
595
-
596
- ```xs
597
- table_trigger "safe_audit" {
598
- table = "sensitive_data"
599
- actions = {insert: true, update: true, delete: true, truncate: false}
600
-
601
- // Uses predefined Table Trigger Input - see Predefined Input Blocks section
602
- input { ... }
603
-
604
- stack {
605
- try_catch {
606
- try {
607
- db.add "audit_log" {
608
- data = {
609
- action: $input.action,
610
- new_data: $input.new,
611
- old_data: $input.old,
612
- timestamp: now
613
- }
614
- }
615
- }
616
- catch {
617
- debug.log { value = "Audit logging failed" }
618
- }
619
- }
620
- }
621
- }
622
- ```
623
-
624
- ### Conditional Trigger Logic
625
-
626
- ```xs
627
- table_trigger "conditional_notification" {
628
- table = "order"
629
- actions = {insert: true, update: false, delete: false, truncate: false}
630
-
631
- // Uses predefined Table Trigger Input - see Predefined Input Blocks section
632
- input { ... }
633
-
634
- stack {
635
- conditional {
636
- if ($input.new.total > 1000) {
637
- util.send_email {
638
- service_provider = "resend"
639
- api_key = $env.RESEND_API_KEY
640
- to = "sales@example.com"
641
- from = "system@example.com"
642
- subject = "High Value Order"
643
- message = "Order #" ~ $input.new.id ~ " for $" ~ $input.new.total
644
- }
645
- }
646
- }
647
- }
648
- }
649
- ```
650
-
651
- ---
652
-
653
592
  ## Best Practices
654
593
 
655
- 1. **Use predefined input blocks as-is** - Each trigger type has a read-only input block that cannot be modified; use the exact structure provided
656
- 2. **Use descriptive names** - Indicate the event and action: `user_audit_log`, `chat_message_handler`
657
- 3. **Handle errors gracefully** - Use try_catch to prevent trigger failures from affecting the main operation
658
- 4. **Keep triggers lightweight** - Offload heavy processing to functions or tasks
659
- 5. **Set appropriate history** - Use `history = false` for high-frequency triggers to save storage
660
- 6. **Use tags** - Organize triggers with meaningful tags for easier management
661
- 7. **Document with description** - Always provide a description explaining the trigger's purpose
662
- 8. **Test thoroughly** - Triggers execute automatically, so ensure they handle edge cases
594
+ 1. **Use predefined input blocks as-is** - Each trigger type has a read-only input block that cannot be modified
595
+ 2. **Handle errors gracefully** - Use try_catch to prevent trigger failures from affecting the main operation
596
+ 3. **Keep triggers lightweight** - Offload heavy processing to functions or tasks; use `history = false` for high-frequency triggers
663
597
 
664
598
  ---
665
599
 
@@ -390,10 +390,9 @@ precondition ($input.start_date < $input.end_date) {
390
390
 
391
391
  ## Best Practices
392
392
 
393
- 1. **Always specify types** - Never leave inputs untyped
394
- 2. **Use filters first** - Prefer declarative filters over stack validation
395
- 3. **Mark sensitive data** - Use `sensitive = true` for PII/credentials
396
- 4. **Validate at boundaries** - Validate user input, trust internal calls
393
+ 1. **Use filters first** - Prefer declarative `filters=` over stack-level preconditions for input validation
394
+ 2. **Mark sensitive data** - Use `sensitive = true` for PII/credentials to mask them in logs
395
+ 3. **Use `?` placement correctly** - `text?` = nullable, `name?` = optional; these are independent modifiers
397
396
 
398
397
  ---
399
398
 
@@ -370,65 +370,11 @@ For a full breakdown of what each required/optional/nullable combination accepts
370
370
 
371
371
  ---
372
372
 
373
- ## Complete Example
374
-
375
- ```xs
376
- function "calculate_discount" {
377
- input {
378
- decimal subtotal filters=min:0
379
- enum customer_type { values = ["regular", "premium", "vip"] }
380
- text coupon?
381
- }
382
- stack {
383
- var $discount_rate { value = 0 }
384
-
385
- switch ($input.customer_type) {
386
- case ("premium") { var.update $discount_rate { value = 0.1 } } break
387
- case ("vip") { var.update $discount_rate { value = 0.2 } } break
388
- default { var.update $discount_rate { value = 0 } }
389
- }
390
-
391
- conditional {
392
- if ($input.coupon == "SAVE10") {
393
- math.add $discount_rate { value = 0.1 }
394
- }
395
- }
396
-
397
- var $discount { value = $input.subtotal * $discount_rate }
398
- }
399
- response = { discount: $discount, rate: $discount_rate }
400
-
401
- test "no discount for regular customer" {
402
- input = { subtotal: 100, customer_type: "regular" }
403
- expect.to_equal ($response.discount) { value = 0 }
404
- }
405
-
406
- test "10% discount for premium" {
407
- input = { subtotal: 100, customer_type: "premium" }
408
- expect.to_equal ($response.discount) { value = 10 }
409
- }
410
-
411
- test "20% discount for VIP" {
412
- input = { subtotal: 100, customer_type: "vip" }
413
- expect.to_equal ($response.discount) { value = 20 }
414
- }
415
-
416
- test "coupon stacks with VIP discount" {
417
- input = { subtotal: 100, customer_type: "vip", coupon: "SAVE10" }
418
- expect.to_equal ($response.rate) { value = 0.3 }
419
- }
420
- }
421
- ```
422
-
423
- ---
424
-
425
373
  ## Best Practices
426
374
 
427
375
  1. **Test happy paths, edge cases, and errors** - Cover expected, boundary, and failure scenarios
428
376
  2. **Use mocks** - Isolate from external dependencies
429
- 3. **Descriptive test names** - Explain what's being tested
430
- 4. **One assertion focus** - Each test verifies one behavior
431
- 5. **Keep tests independent** - No shared state between tests
377
+ 3. **Descriptive test names** - Each test verifies one behavior
432
378
 
433
379
  ---
434
380
 
@@ -181,44 +181,21 @@ expect.to_throw {
181
181
 
182
182
  ## Data Sources
183
183
 
184
- The `datasource` property controls which data source the test runs against.
185
-
186
- **Avoid using `datasource = "live"`.** When a workflow test runs, the entire datasource is cloned so that real data is not modified. This cloning step can be slow, especially for large datasources. Prefer running without a datasource or using a smaller custom datasource instead.
187
-
188
- ### Default (No Data Source) — Recommended
184
+ The `datasource` property controls which data source the test runs against. **Avoid `datasource = "live"`** — it clones the entire datasource before each run, which can be slow. Prefer omitting `datasource` or using a smaller custom datasource.
189
185
 
190
186
  ```xs
187
+ // Recommended: no datasource
191
188
  workflow_test "basic_check" {
192
189
  stack {
193
190
  function.call "health_check" as $result
194
191
  expect.to_be_defined ($result)
195
192
  }
196
193
  }
197
- ```
198
194
 
199
- ### Custom Data Source
200
-
201
- ```xs
202
- workflow_test "staging_data_check" {
195
+ // Custom datasource
196
+ workflow_test "staging_check" {
203
197
  datasource = "staging"
204
- stack {
205
- function.call "get_products" as $products
206
- expect.to_be_defined ($products)
207
- }
208
- }
209
- ```
210
-
211
- ### Live Data Source (Not Recommended)
212
-
213
- Using `datasource = "live"` clones the entire live datasource before running the test. Only use this when you specifically need to validate against production data.
214
-
215
- ```xs
216
- workflow_test "live_data_check" {
217
- datasource = "live"
218
- stack {
219
- function.call "get_active_users" as $users
220
- expect.to_be_defined ($users)
221
- }
198
+ stack { ... }
222
199
  }
223
200
  ```
224
201
 
@@ -317,13 +294,9 @@ workflow_test "comprehensive_test" {
317
294
 
318
295
  ## Best Practices
319
296
 
320
- 1. **Use descriptive names**Name tests after the workflow being tested: `user_signup_flow`, `checkout_process`
321
- 2. **Tag for filtering** — Use tags like `critical`, `e2e`, `smoke` to organize test suites
322
- 3. **Avoid `datasource = "live"`**The entire datasource is cloned before each test run, which can be slow. Use no datasource or a smaller custom datasource instead
323
- 4. **Keep tests independent** — Each workflow test should be self-contained and not depend on other tests
324
- 5. **Use `function.call`, not `function.run`** — `function.call` handles errors so that `expect` assertions work correctly. `function.run` is for calling functions outside of workflow tests
325
- 6. **Use assertions over preconditions** — Prefer `expect.*` assertions for clearer test intent
326
- 7. **Don't test required fields with empty strings** — Required inputs reject both `null` and `""` at the platform level before your stack runs. If you send `""` to a required `text` field, you'll always get a platform validation error — never your custom logic. To test the empty/blank case, the input must be declared optional (`text name?`). See `xanoscript_docs({ topic: "types" })` for the full breakdown.
297
+ 1. **Use `function.call`, not `function.run`** `function.call` handles errors so that `expect` assertions work correctly
298
+ 2. **Keep tests independent** — Each workflow test should be self-contained
299
+ 3. **Don't test required fields with empty strings** Required inputs reject `""` at the platform level. Use optional fields (`text name?`) to test blank cases. See `xanoscript_docs({ topic: "types" })`
327
300
 
328
301
  ---
329
302