@xano/developer-mcp 1.0.1 → 1.0.2

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 (42) hide show
  1. package/README.md +96 -31
  2. package/dist/index.js +248 -180
  3. package/package.json +4 -2
  4. package/xanoscript_docs/README.md +107 -1
  5. package/xanoscript_docs/agents.md +329 -0
  6. package/xanoscript_docs/apis.md +343 -0
  7. package/xanoscript_docs/database.md +417 -0
  8. package/xanoscript_docs/ephemeral.md +333 -0
  9. package/xanoscript_docs/frontend.md +291 -0
  10. package/xanoscript_docs/functions.md +232 -2035
  11. package/xanoscript_docs/integrations.md +439 -0
  12. package/xanoscript_docs/mcp-servers.md +190 -0
  13. package/xanoscript_docs/plan.md +192 -0
  14. package/xanoscript_docs/syntax.md +314 -0
  15. package/xanoscript_docs/tables.md +270 -0
  16. package/xanoscript_docs/tasks.md +254 -0
  17. package/xanoscript_docs/testing.md +335 -0
  18. package/xanoscript_docs/tools.md +305 -0
  19. package/xanoscript_docs/types.md +297 -0
  20. package/xanoscript_docs/version.json +2 -1
  21. package/xanoscript_docs/api_query_examples.md +0 -1255
  22. package/xanoscript_docs/api_query_guideline.md +0 -129
  23. package/xanoscript_docs/build_from_lovable.md +0 -715
  24. package/xanoscript_docs/db_query_guideline.md +0 -427
  25. package/xanoscript_docs/ephemeral_environment_guideline.md +0 -529
  26. package/xanoscript_docs/expression_guideline.md +0 -1086
  27. package/xanoscript_docs/frontend_guideline.md +0 -67
  28. package/xanoscript_docs/function_examples.md +0 -1406
  29. package/xanoscript_docs/function_guideline.md +0 -130
  30. package/xanoscript_docs/input_guideline.md +0 -227
  31. package/xanoscript_docs/mcp_server_examples.md +0 -36
  32. package/xanoscript_docs/mcp_server_guideline.md +0 -69
  33. package/xanoscript_docs/query_filter.md +0 -489
  34. package/xanoscript_docs/table_examples.md +0 -586
  35. package/xanoscript_docs/table_guideline.md +0 -137
  36. package/xanoscript_docs/task_examples.md +0 -511
  37. package/xanoscript_docs/task_guideline.md +0 -103
  38. package/xanoscript_docs/tips_and_tricks.md +0 -144
  39. package/xanoscript_docs/tool_examples.md +0 -69
  40. package/xanoscript_docs/tool_guideline.md +0 -139
  41. package/xanoscript_docs/unit_testing_guideline.md +0 -328
  42. package/xanoscript_docs/workspace.md +0 -17
@@ -0,0 +1,335 @@
1
+ ---
2
+ applyTo: "functions/**/*.xs, apis/**/*.xs"
3
+ ---
4
+
5
+ # Testing
6
+
7
+ Unit tests and mocking in XanoScript.
8
+
9
+ ## Quick Reference
10
+
11
+ ```xs
12
+ function "<name>" {
13
+ input { ... }
14
+ stack { ... }
15
+ response = $result
16
+
17
+ test "<test name>" {
18
+ input = { key: value }
19
+ expect.<assertion> ($response) { value = expected }
20
+ }
21
+ }
22
+ ```
23
+
24
+ ### Assertions
25
+ | Assertion | Purpose |
26
+ |-----------|---------|
27
+ | `expect.to_equal` | Exact match |
28
+ | `expect.to_not_equal` | Not equal |
29
+ | `expect.to_be_true` | Is true |
30
+ | `expect.to_be_false` | Is false |
31
+ | `expect.to_be_null` | Is null |
32
+ | `expect.to_contain` | Array contains |
33
+ | `expect.to_match` | Regex match |
34
+ | `expect.to_throw` | Throws error |
35
+
36
+ ---
37
+
38
+ ## Basic Test
39
+
40
+ ```xs
41
+ function "add" {
42
+ input {
43
+ int a
44
+ int b
45
+ }
46
+ stack {
47
+ var $sum { value = $input.a + $input.b }
48
+ }
49
+ response = $sum
50
+
51
+ test "adds two numbers" {
52
+ input = { a: 5, b: 3 }
53
+ expect.to_equal ($response) { value = 8 }
54
+ }
55
+
56
+ test "handles negative numbers" {
57
+ input = { a: -5, b: 3 }
58
+ expect.to_equal ($response) { value = -2 }
59
+ }
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Assertions
66
+
67
+ ### Value Assertions
68
+
69
+ ```xs
70
+ # Equality
71
+ expect.to_equal ($response.status) { value = "active" }
72
+ expect.to_not_equal ($response.status) { value = "deleted" }
73
+
74
+ # Boolean
75
+ expect.to_be_true ($response.is_active)
76
+ expect.to_be_false ($response.is_deleted)
77
+
78
+ # Null
79
+ expect.to_be_null ($response.deleted_at)
80
+ expect.to_not_be_null ($response.created_at)
81
+
82
+ # Defined
83
+ expect.to_be_defined ($response.id)
84
+ expect.to_not_be_defined ($response.optional_field)
85
+
86
+ # Empty
87
+ expect.to_be_empty ($response.errors)
88
+ ```
89
+
90
+ ### Comparison Assertions
91
+
92
+ ```xs
93
+ # Numeric comparisons
94
+ expect.to_be_greater_than ($response.total) { value = 100 }
95
+ expect.to_be_less_than ($response.stock) { value = 10 }
96
+
97
+ # Range
98
+ expect.to_be_within ($response.temperature) {
99
+ min = 20
100
+ max = 30
101
+ }
102
+ ```
103
+
104
+ ### String Assertions
105
+
106
+ ```xs
107
+ # Starts/ends with
108
+ expect.to_start_with ($response.name) { value = "John" }
109
+ expect.to_end_with ($response.file) { value = ".pdf" }
110
+
111
+ # Contains
112
+ expect.to_contain ($response.tags) { value = "featured" }
113
+
114
+ # Regex match
115
+ expect.to_match ($response.phone) { value = "^\\+1\\d{10}$" }
116
+ ```
117
+
118
+ ### Time Assertions
119
+
120
+ ```xs
121
+ expect.to_be_in_the_past ($response.created_at)
122
+ expect.to_be_in_the_future ($response.expires_at)
123
+ ```
124
+
125
+ ### Error Assertions
126
+
127
+ ```xs
128
+ # Expects any error
129
+ test "throws on invalid input" {
130
+ input = { amount: -1 }
131
+ expect.to_throw
132
+ }
133
+
134
+ # Expects specific error
135
+ test "throws validation error" {
136
+ input = { amount: -1 }
137
+ expect.to_throw { value = "InvalidInputError" }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Mocking
144
+
145
+ Mock external calls to isolate tests:
146
+
147
+ ```xs
148
+ function "get_weather" {
149
+ input { text city }
150
+ stack {
151
+ api.request {
152
+ url = "https://api.weather.com/current"
153
+ params = { city: $input.city }
154
+ mock = {
155
+ "returns sunny for Paris": { response: { weather: "sunny", temp: 22 } },
156
+ "returns rainy for London": { response: { weather: "rainy", temp: 15 } }
157
+ }
158
+ } as $weather
159
+ }
160
+ response = $weather.response
161
+
162
+ test "returns sunny for Paris" {
163
+ input = { city: "Paris" }
164
+ expect.to_equal ($response.weather) { value = "sunny" }
165
+ }
166
+
167
+ test "returns rainy for London" {
168
+ input = { city: "London" }
169
+ expect.to_equal ($response.weather) { value = "rainy" }
170
+ }
171
+ }
172
+ ```
173
+
174
+ ### Mock Matching
175
+
176
+ Mocks activate when their key matches the test name:
177
+
178
+ ```xs
179
+ stack {
180
+ api.request {
181
+ url = "https://api.example.com/data"
182
+ mock = {
183
+ "test name here": { response: { ... } },
184
+ "another test": { response: { ... } }
185
+ }
186
+ } as $result
187
+ }
188
+ ```
189
+
190
+ ### Mocking Variables
191
+
192
+ ```xs
193
+ stack {
194
+ var $message {
195
+ value = "Hello " ~ $input.name
196
+ mock = {
197
+ "custom greeting": "Custom greeting!"
198
+ }
199
+ }
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Testing API Endpoints
206
+
207
+ ```xs
208
+ query "products/{id}" verb=GET {
209
+ input {
210
+ int id { table = "product" }
211
+ }
212
+ stack {
213
+ db.get "product" {
214
+ field_name = "id"
215
+ field_value = $input.id
216
+ mock = {
217
+ "returns product": { id: 1, name: "Widget", price: 29.99 },
218
+ "handles not found": null
219
+ }
220
+ } as $product
221
+ }
222
+ response = $product
223
+
224
+ test "returns product" {
225
+ input = { id: 1 }
226
+ expect.to_equal ($response.name) { value = "Widget" }
227
+ }
228
+
229
+ test "handles not found" {
230
+ input = { id: 999 }
231
+ expect.to_be_null ($response)
232
+ }
233
+ }
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Testing Error Cases
239
+
240
+ ```xs
241
+ function "validate_age" {
242
+ input { int age }
243
+ stack {
244
+ precondition ($input.age >= 0) {
245
+ error_type = "inputerror"
246
+ error = "Age cannot be negative"
247
+ }
248
+ precondition ($input.age <= 150) {
249
+ error_type = "inputerror"
250
+ error = "Age is unrealistic"
251
+ }
252
+ }
253
+ response = { valid: true }
254
+
255
+ test "accepts valid age" {
256
+ input = { age: 25 }
257
+ expect.to_equal ($response.valid) { value = true }
258
+ }
259
+
260
+ test "rejects negative age" {
261
+ input = { age: -5 }
262
+ expect.to_throw { value = "Age cannot be negative" }
263
+ }
264
+
265
+ test "rejects unrealistic age" {
266
+ input = { age: 200 }
267
+ expect.to_throw { value = "Age is unrealistic" }
268
+ }
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Complete Example
275
+
276
+ ```xs
277
+ function "calculate_discount" {
278
+ input {
279
+ decimal subtotal filters=min:0
280
+ enum customer_type { values = ["regular", "premium", "vip"] }
281
+ text coupon?
282
+ }
283
+ stack {
284
+ var $discount_rate { value = 0 }
285
+
286
+ switch ($input.customer_type) {
287
+ case ("premium") { var.update $discount_rate { value = 0.1 } } break
288
+ case ("vip") { var.update $discount_rate { value = 0.2 } } break
289
+ default { var.update $discount_rate { value = 0 } }
290
+ }
291
+
292
+ conditional {
293
+ if ($input.coupon == "SAVE10") {
294
+ math.add $discount_rate { value = 0.1 }
295
+ }
296
+ }
297
+
298
+ var $discount { value = $input.subtotal * $discount_rate }
299
+ }
300
+ response = { discount: $discount, rate: $discount_rate }
301
+
302
+ test "no discount for regular customer" {
303
+ input = { subtotal: 100, customer_type: "regular" }
304
+ expect.to_equal ($response.discount) { value = 0 }
305
+ }
306
+
307
+ test "10% discount for premium" {
308
+ input = { subtotal: 100, customer_type: "premium" }
309
+ expect.to_equal ($response.discount) { value = 10 }
310
+ }
311
+
312
+ test "20% discount for VIP" {
313
+ input = { subtotal: 100, customer_type: "vip" }
314
+ expect.to_equal ($response.discount) { value = 20 }
315
+ }
316
+
317
+ test "coupon stacks with VIP discount" {
318
+ input = { subtotal: 100, customer_type: "vip", coupon: "SAVE10" }
319
+ expect.to_equal ($response.rate) { value = 0.3 }
320
+ }
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Best Practices
327
+
328
+ 1. **Test happy paths** - Verify expected behavior works
329
+ 2. **Test edge cases** - Empty arrays, null values, boundaries
330
+ 3. **Test error cases** - Invalid inputs, missing data
331
+ 4. **Use mocks** - Isolate from external dependencies
332
+ 5. **Descriptive test names** - Explain what's being tested
333
+ 6. **One assertion focus** - Each test verifies one behavior
334
+ 7. **Keep tests independent** - No shared state between tests
335
+ 8. **Test at boundaries** - Min/max values, length limits
@@ -0,0 +1,305 @@
1
+ ---
2
+ applyTo: "tools/**/*.xs"
3
+ ---
4
+
5
+ # Tools
6
+
7
+ Functions that AI agents and MCP servers can execute.
8
+
9
+ ## Quick Reference
10
+
11
+ ```xs
12
+ tool "<name>" {
13
+ description = "Internal documentation"
14
+ instructions = "How the AI should use this tool"
15
+ input { ... }
16
+ stack { ... }
17
+ response = $result
18
+ }
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Basic Structure
24
+
25
+ ```xs
26
+ tool "get_user_by_email" {
27
+ description = "Look up user by email address"
28
+ instructions = "Use this to find user details when you have their email."
29
+
30
+ input {
31
+ email email filters=lower {
32
+ description = "The user's email address"
33
+ }
34
+ }
35
+
36
+ stack {
37
+ db.get "user" {
38
+ field_name = "email"
39
+ field_value = $input.email
40
+ } as $user
41
+ }
42
+
43
+ response = $user
44
+ }
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Key Fields
50
+
51
+ | Field | Purpose | Visibility |
52
+ |-------|---------|------------|
53
+ | `description` | Internal documentation | Not sent to AI |
54
+ | `instructions` | How AI should use tool | Sent to AI |
55
+ | `input` | Parameters with descriptions | Sent to AI |
56
+ | `stack` | Execution logic | Not sent to AI |
57
+ | `response` | Return value | Sent to AI |
58
+
59
+ **Important:** `instructions` and input `description` fields are sent to the AI. Write them clearly.
60
+
61
+ ---
62
+
63
+ ## Input Block
64
+
65
+ Same as functions, but descriptions are especially important:
66
+
67
+ ```xs
68
+ input {
69
+ int order_id {
70
+ description = "The unique order ID to look up"
71
+ }
72
+ enum status {
73
+ description = "New status to set"
74
+ values = ["pending", "processing", "shipped", "delivered"]
75
+ }
76
+ text reason? {
77
+ description = "Optional reason for the status change"
78
+ }
79
+ }
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Tool-Specific Statements
85
+
86
+ ### api.call
87
+ Call an API endpoint:
88
+
89
+ ```xs
90
+ stack {
91
+ api.call "orders/get" verb=GET {
92
+ api_group = "orders"
93
+ input = { order_id: $input.order_id }
94
+ } as $order
95
+ }
96
+ ```
97
+
98
+ ### task.call
99
+ Trigger a background task:
100
+
101
+ ```xs
102
+ stack {
103
+ task.call "send_notification" as $result
104
+ }
105
+ ```
106
+
107
+ ### tool.call
108
+ Call another tool:
109
+
110
+ ```xs
111
+ stack {
112
+ tool.call "get_user_by_id" {
113
+ input = { user_id: $input.user_id }
114
+ } as $user
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Common Patterns
121
+
122
+ ### Database Lookup
123
+ ```xs
124
+ tool "get_order_status" {
125
+ instructions = "Check the current status of an order by its ID."
126
+ input {
127
+ int order_id { description = "Order ID to check" }
128
+ }
129
+ stack {
130
+ db.get "order" {
131
+ field_name = "id"
132
+ field_value = $input.order_id
133
+ } as $order
134
+ }
135
+ response = {
136
+ order_id: $order.id,
137
+ status: $order.status,
138
+ updated_at: $order.updated_at
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Database Update
144
+ ```xs
145
+ tool "update_order_status" {
146
+ instructions = "Update an order's status. Use when customer requests changes."
147
+ input {
148
+ int order_id { description = "Order ID to update" }
149
+ enum status {
150
+ description = "New status"
151
+ values = ["pending", "processing", "shipped", "cancelled"]
152
+ }
153
+ }
154
+ stack {
155
+ db.edit "order" {
156
+ field_name = "id"
157
+ field_value = $input.order_id
158
+ data = { status: $input.status, updated_at: now }
159
+ } as $order
160
+ }
161
+ response = $order
162
+ }
163
+ ```
164
+
165
+ ### External API Call
166
+ ```xs
167
+ tool "get_weather" {
168
+ instructions = "Get current weather for a city."
169
+ input {
170
+ text city filters=trim { description = "City name" }
171
+ }
172
+ stack {
173
+ api.request {
174
+ url = "https://api.weather.com/current"
175
+ method = "GET"
176
+ params = { q: $input.city, key: $env.WEATHER_API_KEY }
177
+ } as $weather
178
+ }
179
+ response = {
180
+ city: $input.city,
181
+ temperature: $weather.temp,
182
+ conditions: $weather.conditions
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### Search Tool
188
+ ```xs
189
+ tool "search_products" {
190
+ instructions = "Search products by name or category."
191
+ input {
192
+ text query? { description = "Search term" }
193
+ text category? { description = "Category filter" }
194
+ int limit?=10 { description = "Max results (default 10)" }
195
+ }
196
+ stack {
197
+ db.query "product" {
198
+ where = $db.product.name includes? $input.query
199
+ && $db.product.category ==? $input.category
200
+ && $db.product.is_active == true
201
+ return = { type: "list", paging: { page: 1, per_page: $input.limit } }
202
+ } as $products
203
+ }
204
+ response = $products.items
205
+ }
206
+ ```
207
+
208
+ ### Create Record
209
+ ```xs
210
+ tool "create_ticket" {
211
+ instructions = "Create a support ticket for the customer."
212
+ input {
213
+ text subject filters=trim { description = "Ticket subject" }
214
+ text description filters=trim { description = "Issue description" }
215
+ enum priority?="medium" {
216
+ description = "Ticket priority"
217
+ values = ["low", "medium", "high", "urgent"]
218
+ }
219
+ }
220
+ stack {
221
+ db.add "ticket" {
222
+ data = {
223
+ subject: $input.subject,
224
+ description: $input.description,
225
+ priority: $input.priority,
226
+ status: "open",
227
+ created_at: now
228
+ }
229
+ } as $ticket
230
+ }
231
+ response = { ticket_id: $ticket.id, message: "Ticket created" }
232
+ }
233
+ ```
234
+
235
+ ### Composed Tool
236
+ ```xs
237
+ tool "get_order_with_items" {
238
+ instructions = "Get complete order details including all items."
239
+ input {
240
+ int order_id { description = "Order ID" }
241
+ }
242
+ stack {
243
+ tool.call "get_order_status" {
244
+ input = { order_id: $input.order_id }
245
+ } as $order
246
+
247
+ db.query "order_item" {
248
+ where = $db.order_item.order_id == $input.order_id
249
+ } as $items
250
+ }
251
+ response = {
252
+ order: $order,
253
+ items: $items
254
+ }
255
+ }
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Error Handling
261
+
262
+ ```xs
263
+ tool "cancel_order" {
264
+ instructions = "Cancel an order. Only works for pending orders."
265
+ input {
266
+ int order_id { description = "Order to cancel" }
267
+ }
268
+ stack {
269
+ db.get "order" {
270
+ field_name = "id"
271
+ field_value = $input.order_id
272
+ } as $order
273
+
274
+ precondition ($order != null) {
275
+ error_type = "notfound"
276
+ error = "Order not found"
277
+ }
278
+
279
+ precondition ($order.status == "pending") {
280
+ error_type = "standard"
281
+ error = "Only pending orders can be cancelled"
282
+ }
283
+
284
+ db.edit "order" {
285
+ field_name = "id"
286
+ field_value = $input.order_id
287
+ data = { status: "cancelled" }
288
+ } as $updated
289
+ }
290
+ response = { success: true, order_id: $input.order_id }
291
+ }
292
+ ```
293
+
294
+ ---
295
+
296
+ ## Best Practices
297
+
298
+ 1. **Write clear instructions** - This is what the AI reads to understand the tool
299
+ 2. **Describe all inputs** - Help AI construct valid requests
300
+ 3. **Use enums for fixed options** - Reduces AI errors
301
+ 4. **Keep tools focused** - One task per tool
302
+ 5. **Handle errors gracefully** - Return clear error messages
303
+ 6. **Validate inputs** - Use filters and preconditions
304
+ 7. **Limit response size** - Don't return huge datasets
305
+ 8. **Use composition** - Call other tools for complex operations