@xano/developer-mcp 1.0.33 → 1.0.34

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.
@@ -21,6 +21,11 @@ export const XANOSCRIPT_DOCS_V2 = {
21
21
  applyTo: ["**/*.xs"],
22
22
  description: "Expressions, operators, and filters for all XanoScript code",
23
23
  },
24
+ quickstart: {
25
+ file: "quickstart.md",
26
+ applyTo: ["**/*.xs"],
27
+ description: "Common patterns, quick reference, and common mistakes to avoid",
28
+ },
24
29
  types: {
25
30
  file: "types.md",
26
31
  applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tools/**/*.xs", "agents/**/*.xs"],
@@ -11,6 +11,7 @@ describe("xanoscript module", () => {
11
11
  const expectedTopics = [
12
12
  "readme",
13
13
  "syntax",
14
+ "quickstart",
14
15
  "types",
15
16
  "tables",
16
17
  "functions",
@@ -135,9 +136,10 @@ describe("xanoscript module", () => {
135
136
  const result = getDocsForFilePath("apis/test.xs");
136
137
  expect(result).not.toContain("readme");
137
138
  });
138
- it("should put syntax first if not already matched", () => {
139
+ it("should include syntax and quickstart for .xs files", () => {
139
140
  const result = getDocsForFilePath("some/random/file.xs");
140
- expect(result[0]).toBe("syntax");
141
+ expect(result).toContain("syntax");
142
+ expect(result).toContain("quickstart");
141
143
  });
142
144
  });
143
145
  describe("extractQuickReference", () => {
@@ -142,18 +142,35 @@ applyTo: "function/**/*.xs"
142
142
 
143
143
  This helps AI tools apply the correct documentation based on the file being edited.
144
144
 
145
+ ## Getting Started
146
+
147
+ For common patterns and quick examples, use:
148
+ ```
149
+ xanoscript_docs({ topic: "quickstart" })
150
+ ```
151
+
152
+ This includes:
153
+ - Variable declaration patterns
154
+ - Conditional logic (if/elseif/else)
155
+ - API requests with error handling
156
+ - Database CRUD operations
157
+ - Common mistakes to avoid
158
+
159
+ ---
160
+
145
161
  ## Documentation Index
146
162
 
147
163
  Use `xanoscript_docs({ topic: "<topic>" })` to retrieve documentation.
148
164
 
149
165
  ### Core Language
150
166
 
151
- | Topic | Description |
152
- | ----------- | ------------------------------------------------- |
153
- | `syntax` | Expressions, operators, filters, system variables |
154
- | `types` | Data types, validation, input blocks |
155
- | `functions` | Reusable function stacks, async, loops |
156
- | `schema` | Runtime schema parsing and validation |
167
+ | Topic | Description |
168
+ | ------------ | ---------------------------------------------------- |
169
+ | `quickstart` | Common patterns, quick examples, mistakes to avoid |
170
+ | `syntax` | Expressions, operators, filters, system variables |
171
+ | `types` | Data types, validation, input blocks |
172
+ | `functions` | Reusable function stacks, async, loops |
173
+ | `schema` | Runtime schema parsing and validation |
157
174
 
158
175
  ### Data
159
176
 
@@ -210,3 +227,47 @@ Use `xanoscript_docs({ topic: "<topic>" })` to retrieve documentation.
210
227
  | ------------- | ------------------------------------------------------------ |
211
228
  | `performance` | Performance optimization best practices |
212
229
  | `security` | Security best practices for authentication and authorization |
230
+
231
+ ---
232
+
233
+ ## Example Implementations
234
+
235
+ Common integration patterns you can reference:
236
+
237
+ ### External API Integrations
238
+ - **OpenAI/ChatGPT**: Use `api.request` with POST to `/v1/chat/completions`
239
+ - **Stripe**: Use `api.request` with form-encoded params for payments
240
+ - **SendGrid/Resend**: Use `api.request` or `util.send_email` for emails
241
+ - **Slack/Discord**: Use `api.request` with webhook URLs
242
+ - **Twilio**: Use `api.request` with Basic auth for SMS
243
+
244
+ ### Common Pattern: API Integration Function
245
+
246
+ ```xs
247
+ function "call_external_api" {
248
+ input {
249
+ text endpoint
250
+ object payload
251
+ }
252
+ stack {
253
+ api.request {
254
+ url = $env.API_BASE_URL ~ $input.endpoint
255
+ method = "POST"
256
+ params = $input.payload
257
+ headers = [
258
+ "Content-Type: application/json",
259
+ "Authorization: Bearer " ~ $env.API_KEY
260
+ ]
261
+ timeout = 30
262
+ } as $api_result
263
+
264
+ precondition ($api_result.response.status >= 200 && $api_result.response.status < 300) {
265
+ error_type = "standard"
266
+ error = "API error: " ~ ($api_result.response.status|to_text)
267
+ }
268
+ }
269
+ response = $api_result.response.result
270
+ }
271
+ ```
272
+
273
+ For more patterns, see `xanoscript_docs({ topic: "quickstart" })` or `xanoscript_docs({ topic: "integrations" })`.
@@ -575,22 +575,92 @@ util.send_email {
575
575
 
576
576
  ---
577
577
 
578
- ## External APIs
578
+ ## External APIs (api.request)
579
579
 
580
580
  Make HTTP requests to external APIs.
581
581
 
582
+ ### Quick Reference
583
+
582
584
  ```xs
583
585
  api.request {
584
- url = "https://api.example.com/data"
585
- method = "POST"
586
- params = { key: "value" }
586
+ url = "https://api.example.com/endpoint"
587
+ method = "POST" // GET, POST, PUT, PATCH, DELETE
588
+ params = $payload // Request body for POST/PUT/PATCH
587
589
  headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
588
- timeout = 30
590
+ timeout = 30 // Timeout in seconds
589
591
  } as $api_result
592
+
593
+ // Access response
594
+ $api_result.response.status // HTTP status code (200, 404, etc.)
595
+ $api_result.response.result // Response body (auto-parsed JSON)
596
+ $api_result.response.headers // Response headers array
590
597
  ```
591
598
 
599
+ > **Important:** The `params` parameter is used for the **request body** (POST/PUT/PATCH), not query parameters. This naming is counterintuitive but consistent across XanoScript.
600
+
592
601
  > **Note:** The `headers` parameter expects an array of text strings, where each string contains the header name and value separated by a colon (e.g., `["Content-Type: application/json", "X-Custom-Header: value"]`).
593
602
 
603
+ ### GET Request
604
+
605
+ ```xs
606
+ api.request {
607
+ url = "https://api.example.com/users?page=1&limit=10"
608
+ method = "GET"
609
+ headers = ["Authorization: Bearer " ~ $env.API_KEY]
610
+ } as $api_result
611
+
612
+ var $users { value = $api_result.response.result }
613
+ ```
614
+
615
+ ### POST Request with JSON Body
616
+
617
+ ```xs
618
+ var $payload {
619
+ value = {
620
+ name: $input.name,
621
+ email: $input.email
622
+ }
623
+ }
624
+
625
+ api.request {
626
+ url = "https://api.example.com/users"
627
+ method = "POST"
628
+ params = $payload
629
+ headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
630
+ } as $api_result
631
+
632
+ // Check for success
633
+ precondition ($api_result.response.status == 201) {
634
+ error_type = "standard"
635
+ error = "Failed to create user: " ~ ($api_result.response.result|json_encode)
636
+ }
637
+ ```
638
+
639
+ ### Error Handling Pattern
640
+
641
+ ```xs
642
+ api.request {
643
+ url = "https://api.example.com/data"
644
+ method = "GET"
645
+ timeout = 30
646
+ } as $api_result
647
+
648
+ conditional {
649
+ if ($api_result.response.status >= 200 && $api_result.response.status < 300) {
650
+ var $data { value = $api_result.response.result }
651
+ }
652
+ elseif ($api_result.response.status == 404) {
653
+ throw { name = "NotFound", value = "Resource not found" }
654
+ }
655
+ else {
656
+ throw {
657
+ name = "APIError",
658
+ value = "API returned status " ~ ($api_result.response.status|to_text)
659
+ }
660
+ }
661
+ }
662
+ ```
663
+
594
664
  ### Response Structure
595
665
 
596
666
  The `api.request` statement returns an object with both request and response details:
@@ -0,0 +1,340 @@
1
+ ---
2
+ applyTo: "**/*.xs"
3
+ ---
4
+
5
+ # Quickstart & Common Patterns
6
+
7
+ Essential patterns for XanoScript development. Use this as a quick reference for frequently-used code patterns.
8
+
9
+ ## Quick Reference
10
+
11
+ ### Variable Declaration
12
+ ```xs
13
+ var $name { value = "initial value" }
14
+ var $count { value = 0 }
15
+ var $data { value = { key: "value" } }
16
+ ```
17
+
18
+ ### Conditional Logic
19
+ ```xs
20
+ conditional {
21
+ if (`$status == "active"`) {
22
+ var $result { value = "Active user" }
23
+ }
24
+ elseif (`$status == "pending"`) {
25
+ var $result { value = "Pending approval" }
26
+ }
27
+ else {
28
+ var $result { value = "Unknown status" }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### API Request with Error Handling
34
+ ```xs
35
+ api.request {
36
+ url = "https://api.example.com/data"
37
+ method = "POST"
38
+ params = $payload
39
+ headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
40
+ } as $api_result
41
+
42
+ precondition ($api_result.response.status == 200) {
43
+ error_type = "standard"
44
+ error = "API request failed"
45
+ }
46
+
47
+ var $data { value = $api_result.response.result }
48
+ ```
49
+
50
+ ### String Concatenation
51
+ ```xs
52
+ var $greeting { value = "Hello, " ~ $input.name ~ "!" }
53
+
54
+ // With filters (use parentheses)
55
+ var $message { value = "Status: " ~ ($status|to_text) ~ " - " ~ ($data|json_encode) }
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Common Patterns
61
+
62
+ ### 1. Input Validation
63
+
64
+ ```xs
65
+ // Required field check
66
+ precondition ($input.email != null && $input.email != "") {
67
+ error_type = "inputerror"
68
+ error = "Email is required"
69
+ }
70
+
71
+ // Format validation
72
+ precondition ($input.email|contains:"@") {
73
+ error_type = "inputerror"
74
+ error = "Invalid email format"
75
+ }
76
+ ```
77
+
78
+ ### 2. Database CRUD
79
+
80
+ ```xs
81
+ // Create
82
+ db.add "user" {
83
+ data = { name: $input.name, email: $input.email }
84
+ } as $new_user
85
+
86
+ // Read one
87
+ db.get "user" {
88
+ where = $db.user.id == $input.id
89
+ } as $user
90
+
91
+ // Read many
92
+ db.query "user" {
93
+ where = $db.user.is_active == true
94
+ order = [{ field: created_at, direction: desc }]
95
+ paging = { limit: 10, offset: 0 }
96
+ } as $users
97
+
98
+ // Update
99
+ db.edit "user" {
100
+ where = $db.user.id == $input.id
101
+ data = { name: $input.name }
102
+ }
103
+
104
+ // Delete
105
+ db.delete "user" {
106
+ where = $db.user.id == $input.id
107
+ }
108
+ ```
109
+
110
+ ### 3. Optional Field Handling
111
+
112
+ ```xs
113
+ // Build object with optional fields
114
+ var $data { value = { required_field: $input.required } }
115
+
116
+ conditional {
117
+ if ($input.optional_field != null) {
118
+ var.update $data { value = $data|set:"optional_field":$input.optional_field }
119
+ }
120
+ }
121
+
122
+ // Or use set_ifnotnull
123
+ var $data {
124
+ value = { required: $input.required }|set_ifnotnull:"optional":$input.optional
125
+ }
126
+ ```
127
+
128
+ ### 4. Loop Through Array
129
+
130
+ ```xs
131
+ // Using each
132
+ each ($items as $item) {
133
+ debug.log { value = $item.name }
134
+ }
135
+
136
+ // Using map filter
137
+ var $names { value = $items|map:$$.name }
138
+
139
+ // Using filter
140
+ var $active_items { value = $items|filter:$$.is_active == true }
141
+ ```
142
+
143
+ ### 5. Error Handling with Try-Catch
144
+
145
+ ```xs
146
+ try_catch {
147
+ try {
148
+ api.request {
149
+ url = "https://api.example.com/risky"
150
+ method = "GET"
151
+ } as $result
152
+ }
153
+ catch {
154
+ debug.log { value = "Request failed, using fallback" }
155
+ var $result { value = { response: { result: null } } }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### 6. Authentication Check
161
+
162
+ ```xs
163
+ // Require authenticated user
164
+ precondition ($auth.id != null) {
165
+ error_type = "accessdenied"
166
+ error = "Authentication required"
167
+ }
168
+
169
+ // Check user owns resource
170
+ db.get "post" {
171
+ where = $db.post.id == $input.post_id
172
+ } as $post
173
+
174
+ precondition ($post.user_id == $auth.id) {
175
+ error_type = "accessdenied"
176
+ error = "You can only edit your own posts"
177
+ }
178
+ ```
179
+
180
+ ### 7. Pagination
181
+
182
+ ```xs
183
+ var $page { value = $input.page ?? 1 }
184
+ var $limit { value = $input.limit ?? 20 }
185
+ var $offset { value = ($page - 1) * $limit }
186
+
187
+ db.query "item" {
188
+ where = $db.item.is_active == true
189
+ order = [{ field: created_at, direction: desc }]
190
+ paging = { limit: $limit, offset: $offset }
191
+ count = true
192
+ } as $result
193
+
194
+ response = {
195
+ items: $result.items,
196
+ total: $result.count,
197
+ page: $page,
198
+ pages: ($result.count / $limit)|ceil
199
+ }
200
+ ```
201
+
202
+ ### 8. Building Complex Objects
203
+
204
+ ```xs
205
+ // Step by step
206
+ var $response { value = {} }
207
+ var.update $response { value = $response|set:"user":$user }
208
+ var.update $response { value = $response|set:"posts":$posts }
209
+ var.update $response { value = $response|set:"stats":{ count: $posts|count } }
210
+
211
+ // Or all at once
212
+ var $response {
213
+ value = {
214
+ user: $user,
215
+ posts: $posts,
216
+ stats: { count: $posts|count }
217
+ }
218
+ }
219
+ ```
220
+
221
+ ### 9. Date/Time Operations
222
+
223
+ ```xs
224
+ // Current timestamp
225
+ var $now { value = now }
226
+
227
+ // Format for display
228
+ var $formatted { value = now|format_timestamp:"Y-m-d H:i:s":"UTC" }
229
+
230
+ // Relative time
231
+ var $yesterday { value = now|transform_timestamp:"-1 day" }
232
+ var $next_week { value = now|transform_timestamp:"+7 days" }
233
+
234
+ // Compare dates
235
+ db.query "event" {
236
+ where = $db.event.start_date >= now
237
+ } as $upcoming_events
238
+ ```
239
+
240
+ ### 10. JSON API Response
241
+
242
+ ```xs
243
+ api.request {
244
+ url = "https://api.openai.com/v1/chat/completions"
245
+ method = "POST"
246
+ params = {
247
+ model: "gpt-4",
248
+ messages: [{ role: "user", content: $input.prompt }]
249
+ }
250
+ headers = [
251
+ "Content-Type: application/json",
252
+ "Authorization: Bearer " ~ $env.OPENAI_API_KEY
253
+ ]
254
+ } as $api_result
255
+
256
+ conditional {
257
+ if ($api_result.response.status == 200) {
258
+ var $answer { value = $api_result.response.result.choices|first|get:"message"|get:"content" }
259
+ }
260
+ else {
261
+ throw {
262
+ name = "APIError",
263
+ value = "OpenAI API error: " ~ ($api_result.response.status|to_text)
264
+ }
265
+ }
266
+ }
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Common Mistakes
272
+
273
+ ### 1. Using `else if` instead of `elseif`
274
+ ```xs
275
+ // ❌ Wrong
276
+ conditional {
277
+ if (...) { }
278
+ else if (...) { } // Parse error!
279
+ }
280
+
281
+ // ✅ Correct
282
+ conditional {
283
+ if (...) { }
284
+ elseif (...) { }
285
+ }
286
+ ```
287
+
288
+ ### 2. Missing parentheses in filter concatenation
289
+ ```xs
290
+ // ❌ Wrong
291
+ var $msg { value = $status|to_text ~ " - " ~ $data|json_encode }
292
+
293
+ // ✅ Correct
294
+ var $msg { value = ($status|to_text) ~ " - " ~ ($data|json_encode) }
295
+ ```
296
+
297
+ ### 3. Using `body` instead of `params` for api.request
298
+ ```xs
299
+ // ❌ Wrong
300
+ api.request {
301
+ url = "..."
302
+ method = "POST"
303
+ body = $payload // "body" is not valid!
304
+ }
305
+
306
+ // ✅ Correct
307
+ api.request {
308
+ url = "..."
309
+ method = "POST"
310
+ params = $payload // Use "params" for request body
311
+ }
312
+ ```
313
+
314
+ ### 4. Using `default` filter (doesn't exist)
315
+ ```xs
316
+ // ❌ Wrong
317
+ var $value { value = $input.optional|default:"fallback" }
318
+
319
+ // ✅ Correct
320
+ var $value { value = $input.optional|first_notnull:"fallback" }
321
+ // or
322
+ var $value { value = $input.optional ?? "fallback" }
323
+ ```
324
+
325
+ ### 5. Using $env in run.job input blocks
326
+ ```xs
327
+ // ❌ Wrong - $env not allowed in input blocks
328
+ run.job "my_job" {
329
+ input {
330
+ text api_key = $env.API_KEY
331
+ }
332
+ }
333
+
334
+ // ✅ Correct - use $env in the stack
335
+ run.job "my_job" {
336
+ stack {
337
+ var $api_key { value = $env.API_KEY }
338
+ }
339
+ }
340
+ ```
@@ -21,12 +21,67 @@ Complete reference for XanoScript expressions, operators, and filters.
21
21
  | Filter | Purpose | Example |
22
22
  |--------|---------|---------|
23
23
  | `trim` | Remove whitespace | `$s\|trim` |
24
- | `lower` / `upper` | Case conversion | `$s\|to_lower` |
24
+ | `to_lower` / `to_upper` | Case conversion | `$s\|to_lower` |
25
25
  | `first` / `last` | Array endpoints | `$arr\|first` |
26
26
  | `count` | Array/object length | `$arr\|count` |
27
27
  | `get` | Object property | `$obj\|get:"key"` |
28
28
  | `set` | Set property | `$obj\|set:"key":"val"` |
29
29
  | `json_encode` / `json_decode` | JSON conversion | `$obj\|json_encode` |
30
+ | `to_text` / `to_int` | Type conversion | `$num\|to_text` |
31
+
32
+ > **Note:** There is no `default` filter. Use conditional blocks or `first_notnull`/`first_notempty` instead.
33
+
34
+ ### String Concatenation with Filters
35
+
36
+ When concatenating strings that use filters, wrap each filtered expression in parentheses:
37
+
38
+ ```xs
39
+ // ✅ Correct - parentheses around filtered expressions
40
+ var $message {
41
+ value = ($status|to_text) ~ ": " ~ ($data|json_encode)
42
+ }
43
+
44
+ // ❌ Incorrect - missing parentheses causes parse error
45
+ var $message {
46
+ value = $status|to_text ~ ": " ~ $data|json_encode
47
+ }
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Conditional Blocks
53
+
54
+ Use `conditional` blocks for if/elseif/else logic:
55
+
56
+ ```xs
57
+ conditional {
58
+ if (`$status == "success"`) {
59
+ var $message { value = "All good!" }
60
+ }
61
+ elseif (`$status == "pending"`) {
62
+ var $message { value = "Please wait..." }
63
+ }
64
+ else {
65
+ var $message { value = "Unknown status" }
66
+ }
67
+ }
68
+ ```
69
+
70
+ > **Important:** Use `elseif` (one word), not `else if` or `else { if (...) }`. Nested `if` inside `else` blocks is not supported.
71
+
72
+ ### Conditional as Expression
73
+
74
+ Use conditional blocks inline to return values:
75
+
76
+ ```xs
77
+ var $tier_limit {
78
+ value = conditional {
79
+ if ($auth.tier == "premium") { 1000 }
80
+ elseif ($auth.tier == "pro") { 500 }
81
+ else { 100 }
82
+ }
83
+ }
84
+ ```
30
85
 
31
86
  ---
32
87
 
@@ -431,6 +486,32 @@ var $client_ip { value = $env.$remote_ip }
431
486
  var $method { value = $env.$request_method }
432
487
  var $headers { value = $env.$http_headers }
433
488
  var $current_branch { value = $env.$branch }
489
+
490
+ // Custom environment variables (set in Xano dashboard)
491
+ var $api_key { value = $env.MY_API_KEY }
492
+ ```
493
+
494
+ ### $env Limitations
495
+
496
+ > **Important:** `$env` variables cannot be used in `run.job` or `run.service` input blocks. Input values must be constants.
497
+
498
+ ```xs
499
+ // ❌ Invalid - $env not allowed in run.job input
500
+ run.job "my_job" {
501
+ input {
502
+ text api_key = $env.API_KEY // Error!
503
+ }
504
+ }
505
+
506
+ // ✅ Valid - access $env inside the stack instead
507
+ run.job "my_job" {
508
+ input {
509
+ text api_key // No default value
510
+ }
511
+ stack {
512
+ var $key { value = $env.API_KEY }
513
+ }
514
+ }
434
515
  ```
435
516
 
436
517
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/developer-mcp",
3
- "version": "1.0.33",
3
+ "version": "1.0.34",
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",