@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.
- package/README.md +96 -31
- package/dist/index.js +248 -180
- package/package.json +4 -2
- package/xanoscript_docs/README.md +107 -1
- package/xanoscript_docs/agents.md +329 -0
- package/xanoscript_docs/apis.md +343 -0
- package/xanoscript_docs/database.md +417 -0
- package/xanoscript_docs/ephemeral.md +333 -0
- package/xanoscript_docs/frontend.md +291 -0
- package/xanoscript_docs/functions.md +232 -2035
- package/xanoscript_docs/integrations.md +439 -0
- package/xanoscript_docs/mcp-servers.md +190 -0
- package/xanoscript_docs/plan.md +192 -0
- package/xanoscript_docs/syntax.md +314 -0
- package/xanoscript_docs/tables.md +270 -0
- package/xanoscript_docs/tasks.md +254 -0
- package/xanoscript_docs/testing.md +335 -0
- package/xanoscript_docs/tools.md +305 -0
- package/xanoscript_docs/types.md +297 -0
- package/xanoscript_docs/version.json +2 -1
- package/xanoscript_docs/api_query_examples.md +0 -1255
- package/xanoscript_docs/api_query_guideline.md +0 -129
- package/xanoscript_docs/build_from_lovable.md +0 -715
- package/xanoscript_docs/db_query_guideline.md +0 -427
- package/xanoscript_docs/ephemeral_environment_guideline.md +0 -529
- package/xanoscript_docs/expression_guideline.md +0 -1086
- package/xanoscript_docs/frontend_guideline.md +0 -67
- package/xanoscript_docs/function_examples.md +0 -1406
- package/xanoscript_docs/function_guideline.md +0 -130
- package/xanoscript_docs/input_guideline.md +0 -227
- package/xanoscript_docs/mcp_server_examples.md +0 -36
- package/xanoscript_docs/mcp_server_guideline.md +0 -69
- package/xanoscript_docs/query_filter.md +0 -489
- package/xanoscript_docs/table_examples.md +0 -586
- package/xanoscript_docs/table_guideline.md +0 -137
- package/xanoscript_docs/task_examples.md +0 -511
- package/xanoscript_docs/task_guideline.md +0 -103
- package/xanoscript_docs/tips_and_tricks.md +0 -144
- package/xanoscript_docs/tool_examples.md +0 -69
- package/xanoscript_docs/tool_guideline.md +0 -139
- package/xanoscript_docs/unit_testing_guideline.md +0 -328
- package/xanoscript_docs/workspace.md +0 -17
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "apis/**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# APIs
|
|
6
|
+
|
|
7
|
+
HTTP endpoint definitions in XanoScript.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
```xs
|
|
12
|
+
query "<path>" verb=<METHOD> {
|
|
13
|
+
description = "What this endpoint does"
|
|
14
|
+
auth = "<table>" # Optional: require authentication
|
|
15
|
+
input { ... }
|
|
16
|
+
stack { ... }
|
|
17
|
+
response = $result
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### HTTP Methods
|
|
22
|
+
`GET`, `POST`, `PUT`, `PATCH`, `DELETE`
|
|
23
|
+
|
|
24
|
+
### File Structure
|
|
25
|
+
```
|
|
26
|
+
apis/
|
|
27
|
+
├── users/ # API group
|
|
28
|
+
│ ├── list.xs # GET /users
|
|
29
|
+
│ ├── create.xs # POST /users
|
|
30
|
+
│ └── {id}.xs # GET/PATCH/DELETE /users/{id}
|
|
31
|
+
└── products/
|
|
32
|
+
└── search.xs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Basic Structure
|
|
38
|
+
|
|
39
|
+
```xs
|
|
40
|
+
query "products" verb=GET {
|
|
41
|
+
description = "List all products"
|
|
42
|
+
input {
|
|
43
|
+
int page?=1 filters=min:1
|
|
44
|
+
int per_page?=20 filters=min:1|max:100
|
|
45
|
+
}
|
|
46
|
+
stack {
|
|
47
|
+
db.query "product" {
|
|
48
|
+
return = { type: "list", paging: { page: $input.page, per_page: $input.per_page } }
|
|
49
|
+
} as $products
|
|
50
|
+
}
|
|
51
|
+
response = $products
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Authentication
|
|
58
|
+
|
|
59
|
+
### Public Endpoint (default)
|
|
60
|
+
```xs
|
|
61
|
+
query "status" verb=GET {
|
|
62
|
+
stack { }
|
|
63
|
+
response = { status: "ok" }
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Authenticated Endpoint
|
|
68
|
+
```xs
|
|
69
|
+
query "profile" verb=GET {
|
|
70
|
+
auth = "user" # Requires valid JWT
|
|
71
|
+
stack {
|
|
72
|
+
db.get "user" {
|
|
73
|
+
field_name = "id"
|
|
74
|
+
field_value = $auth.id # User ID from token
|
|
75
|
+
} as $user
|
|
76
|
+
}
|
|
77
|
+
response = $user
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
When `auth` is set:
|
|
82
|
+
- Endpoint requires Bearer token in `Authorization` header
|
|
83
|
+
- `$auth.id` contains authenticated user's ID
|
|
84
|
+
- Invalid/missing token returns 401
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Path Parameters
|
|
89
|
+
|
|
90
|
+
Use `{param}` in the path:
|
|
91
|
+
|
|
92
|
+
```xs
|
|
93
|
+
query "users/{user_id}" verb=GET {
|
|
94
|
+
auth = "user"
|
|
95
|
+
input {
|
|
96
|
+
int user_id { table = "user" }
|
|
97
|
+
}
|
|
98
|
+
stack {
|
|
99
|
+
db.get "user" {
|
|
100
|
+
field_name = "id"
|
|
101
|
+
field_value = $input.user_id
|
|
102
|
+
} as $user
|
|
103
|
+
}
|
|
104
|
+
response = $user
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## CRUD Examples
|
|
111
|
+
|
|
112
|
+
### List (GET)
|
|
113
|
+
```xs
|
|
114
|
+
query "products" verb=GET {
|
|
115
|
+
input {
|
|
116
|
+
text category? filters=trim|lower
|
|
117
|
+
int page?=1
|
|
118
|
+
int per_page?=20
|
|
119
|
+
}
|
|
120
|
+
stack {
|
|
121
|
+
db.query "product" {
|
|
122
|
+
where = $db.product.category ==? $input.category
|
|
123
|
+
sort = { created_at: "desc" }
|
|
124
|
+
return = { type: "list", paging: { page: $input.page, per_page: $input.per_page } }
|
|
125
|
+
} as $products
|
|
126
|
+
}
|
|
127
|
+
response = $products
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Create (POST)
|
|
132
|
+
```xs
|
|
133
|
+
query "products" verb=POST {
|
|
134
|
+
auth = "user"
|
|
135
|
+
input {
|
|
136
|
+
text name filters=trim
|
|
137
|
+
text description? filters=trim
|
|
138
|
+
decimal price filters=min:0
|
|
139
|
+
int category_id { table = "category" }
|
|
140
|
+
}
|
|
141
|
+
stack {
|
|
142
|
+
db.add "product" {
|
|
143
|
+
data = {
|
|
144
|
+
name: $input.name,
|
|
145
|
+
description: $input.description,
|
|
146
|
+
price: $input.price,
|
|
147
|
+
category_id: $input.category_id,
|
|
148
|
+
created_by: $auth.id
|
|
149
|
+
}
|
|
150
|
+
} as $product
|
|
151
|
+
}
|
|
152
|
+
response = $product
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Read (GET with ID)
|
|
157
|
+
```xs
|
|
158
|
+
query "products/{product_id}" verb=GET {
|
|
159
|
+
input {
|
|
160
|
+
int product_id { table = "product" }
|
|
161
|
+
}
|
|
162
|
+
stack {
|
|
163
|
+
db.get "product" {
|
|
164
|
+
field_name = "id"
|
|
165
|
+
field_value = $input.product_id
|
|
166
|
+
} as $product
|
|
167
|
+
|
|
168
|
+
precondition ($product != null) {
|
|
169
|
+
error_type = "notfound"
|
|
170
|
+
error = "Product not found"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
response = $product
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Update (PATCH)
|
|
178
|
+
```xs
|
|
179
|
+
query "products/{product_id}" verb=PATCH {
|
|
180
|
+
auth = "user"
|
|
181
|
+
input {
|
|
182
|
+
int product_id { table = "product" }
|
|
183
|
+
text name? filters=trim
|
|
184
|
+
text description? filters=trim
|
|
185
|
+
decimal price? filters=min:0
|
|
186
|
+
}
|
|
187
|
+
stack {
|
|
188
|
+
var $updates { value = {} }
|
|
189
|
+
|
|
190
|
+
conditional {
|
|
191
|
+
if ($input.name != null) {
|
|
192
|
+
var.update $updates { value = $updates|set:"name":$input.name }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
conditional {
|
|
196
|
+
if ($input.price != null) {
|
|
197
|
+
var.update $updates { value = $updates|set:"price":$input.price }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
db.patch "product" {
|
|
202
|
+
field_name = "id"
|
|
203
|
+
field_value = $input.product_id
|
|
204
|
+
data = $updates
|
|
205
|
+
} as $product
|
|
206
|
+
}
|
|
207
|
+
response = $product
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Delete (DELETE)
|
|
212
|
+
```xs
|
|
213
|
+
query "products/{product_id}" verb=DELETE {
|
|
214
|
+
auth = "user"
|
|
215
|
+
input {
|
|
216
|
+
int product_id { table = "product" }
|
|
217
|
+
}
|
|
218
|
+
stack {
|
|
219
|
+
db.del "product" {
|
|
220
|
+
field_name = "id"
|
|
221
|
+
field_value = $input.product_id
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
response = { success: true }
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Response Types
|
|
231
|
+
|
|
232
|
+
### JSON (default)
|
|
233
|
+
```xs
|
|
234
|
+
response = $data
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### HTML
|
|
238
|
+
```xs
|
|
239
|
+
stack {
|
|
240
|
+
util.set_header {
|
|
241
|
+
value = "Content-Type: text/html; charset=utf-8"
|
|
242
|
+
duplicates = "replace"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
util.template_engine {
|
|
246
|
+
value = """
|
|
247
|
+
<html>
|
|
248
|
+
<body><h1>{{ $var.title }}</h1></body>
|
|
249
|
+
</html>
|
|
250
|
+
"""
|
|
251
|
+
} as $html
|
|
252
|
+
}
|
|
253
|
+
response = $html
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Streaming
|
|
257
|
+
```xs
|
|
258
|
+
stack {
|
|
259
|
+
api.stream { value = $processed_data }
|
|
260
|
+
}
|
|
261
|
+
response = null
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Custom Headers
|
|
267
|
+
|
|
268
|
+
```xs
|
|
269
|
+
stack {
|
|
270
|
+
util.set_header {
|
|
271
|
+
value = "X-Custom-Header: value"
|
|
272
|
+
duplicates = "replace"
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
util.set_header {
|
|
276
|
+
value = "Set-Cookie: session=abc123; HttpOnly; Secure"
|
|
277
|
+
duplicates = "add"
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Error Handling
|
|
285
|
+
|
|
286
|
+
### Preconditions
|
|
287
|
+
```xs
|
|
288
|
+
stack {
|
|
289
|
+
precondition ($input.amount > 0) {
|
|
290
|
+
error_type = "inputerror"
|
|
291
|
+
error = "Amount must be positive"
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
precondition ($user != null) {
|
|
295
|
+
error_type = "notfound"
|
|
296
|
+
error = "User not found"
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
precondition ($user.id == $auth.id) {
|
|
300
|
+
error_type = "accessdenied"
|
|
301
|
+
error = "Not authorized"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Error Types
|
|
307
|
+
| Type | HTTP Status |
|
|
308
|
+
|------|-------------|
|
|
309
|
+
| `inputerror` | 400 Bad Request |
|
|
310
|
+
| `accessdenied` | 403 Forbidden |
|
|
311
|
+
| `notfound` | 404 Not Found |
|
|
312
|
+
| `standard` | 500 Internal Server Error |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Pagination Response Format
|
|
317
|
+
|
|
318
|
+
When using `return = { type: "list", paging: {...} }`:
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"itemsReceived": 20,
|
|
323
|
+
"curPage": 1,
|
|
324
|
+
"nextPage": 2,
|
|
325
|
+
"prevPage": null,
|
|
326
|
+
"offset": 0,
|
|
327
|
+
"perPage": 20,
|
|
328
|
+
"items": [...]
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Best Practices
|
|
335
|
+
|
|
336
|
+
1. **RESTful design** - Use appropriate HTTP methods
|
|
337
|
+
2. **Consistent naming** - Use lowercase, hyphens for multi-word paths
|
|
338
|
+
3. **Authenticate sensitive operations** - Always auth for writes
|
|
339
|
+
4. **Validate inputs** - Use filters and preconditions
|
|
340
|
+
5. **Return appropriate errors** - Use correct error types
|
|
341
|
+
6. **Paginate lists** - Never return unbounded lists
|
|
342
|
+
7. **Document with description** - Explain what endpoint does
|
|
343
|
+
8. **Group related endpoints** - Organize by resource in api groups
|