@xano/developer-mcp 1.0.5 → 1.0.7

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.
@@ -0,0 +1,532 @@
1
+ ---
2
+ applyTo: "triggers/**/*.xs"
3
+ ---
4
+
5
+ # Triggers
6
+
7
+ Event-driven handlers that execute in response to system events. Triggers allow you to react to database changes, real-time messages, workspace events, agent connections, and MCP server tool calls.
8
+
9
+ ## Quick Reference
10
+
11
+ | Trigger Type | Purpose | Required Clauses |
12
+ |--------------|---------|------------------|
13
+ | `table_trigger` | React to database table changes | `table`, `input`, `stack` |
14
+ | `realtime_trigger` | Handle real-time channel events | `channel`, `input`, `stack`, `response` |
15
+ | `workspace_trigger` | React to branch lifecycle events | `input`, `stack` |
16
+ | `agent_trigger` | Handle AI agent connections | `agent`, `input`, `stack`, `response` |
17
+ | `mcp_server_trigger` | Handle MCP server tool calls | `mcp_server`, `input`, `stack`, `response` |
18
+
19
+ ---
20
+
21
+ ## Table Trigger
22
+
23
+ Executes when database table records are inserted, updated, deleted, or truncated.
24
+
25
+ ### Syntax
26
+
27
+ ```xs
28
+ table_trigger "<name>" {
29
+ table = "<table_name>"
30
+ actions = {insert: true, update: true, delete: true, truncate: false}
31
+ datasources = ["datasource1", "datasource2"]
32
+ active = true
33
+ description = "Description of this trigger"
34
+ tags = ["tag1", "tag2"]
35
+
36
+ input {
37
+ // Define input parameters
38
+ }
39
+
40
+ stack {
41
+ // Logic to execute when triggered
42
+ }
43
+
44
+ history = 100
45
+ }
46
+ ```
47
+
48
+ ### Required Clauses
49
+
50
+ | Clause | Description |
51
+ |--------|-------------|
52
+ | `table` | The database table name to monitor |
53
+ | `input` | Input parameter definitions |
54
+ | `stack` | Logic to execute when triggered |
55
+
56
+ ### Optional Clauses
57
+
58
+ | Clause | Type | Description |
59
+ |--------|------|-------------|
60
+ | `actions` | object | Which operations trigger execution |
61
+ | `active` | boolean | Enable/disable the trigger |
62
+ | `datasources` | array | List of datasources to apply trigger to |
63
+ | `description` | string | Human-readable description |
64
+ | `history` | integer/string | History retention setting |
65
+ | `tags` | array | Tags for organization |
66
+
67
+ ### Actions
68
+
69
+ | Action | Description |
70
+ |--------|-------------|
71
+ | `insert` | Trigger on new record creation |
72
+ | `update` | Trigger on record modification |
73
+ | `delete` | Trigger on record deletion |
74
+ | `truncate` | Trigger when table is truncated |
75
+
76
+ ### Example
77
+
78
+ ```xs
79
+ table_trigger "audit_user_changes" {
80
+ table = "user"
81
+ actions = {insert: true, update: true, delete: true, truncate: false}
82
+ active = true
83
+ description = "Log all changes to user records"
84
+ datasources = ["main_db"]
85
+
86
+ input {
87
+ }
88
+
89
+ stack {
90
+ db.add "audit_log" {
91
+ data = {
92
+ table_name: "user",
93
+ action: $trigger.action,
94
+ record_id: $trigger.record.id,
95
+ timestamp: now
96
+ }
97
+ }
98
+ }
99
+
100
+ history = 100
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Realtime Trigger
107
+
108
+ Handles events from real-time channels, such as client joins or messages.
109
+
110
+ ### Syntax
111
+
112
+ ```xs
113
+ realtime_trigger "<name>" {
114
+ channel = "<channel_name>"
115
+ actions = {join: true, message: true}
116
+ active = true
117
+ description = "Description of this trigger"
118
+ tags = ["tag1", "tag2"]
119
+
120
+ input {
121
+ // Define input parameters
122
+ }
123
+
124
+ stack {
125
+ // Logic to execute when triggered
126
+ }
127
+
128
+ response = $result
129
+
130
+ history = 100
131
+ }
132
+ ```
133
+
134
+ ### Required Clauses
135
+
136
+ | Clause | Description |
137
+ |--------|-------------|
138
+ | `channel` | The real-time channel to monitor |
139
+ | `input` | Input parameter definitions |
140
+ | `stack` | Logic to execute when triggered |
141
+ | `response` | Value to return to the client |
142
+
143
+ ### Optional Clauses
144
+
145
+ | Clause | Type | Description |
146
+ |--------|------|-------------|
147
+ | `actions` | object | Which events trigger execution |
148
+ | `active` | boolean | Enable/disable the trigger |
149
+ | `description` | string | Human-readable description |
150
+ | `history` | integer/string | History retention setting |
151
+ | `tags` | array | Tags for organization |
152
+
153
+ ### Actions
154
+
155
+ | Action | Description |
156
+ |--------|-------------|
157
+ | `join` | Trigger when a client joins the channel |
158
+ | `message` | Trigger when a message is received |
159
+
160
+ ### Example
161
+
162
+ ```xs
163
+ realtime_trigger "chat_message_handler" {
164
+ channel = "chat_room"
165
+ actions = {join: true, message: true}
166
+ active = true
167
+ description = "Handle chat room messages and joins"
168
+
169
+ input {
170
+ text message filters=trim
171
+ }
172
+
173
+ stack {
174
+ conditional {
175
+ if ($trigger.action == "join") {
176
+ var $welcome { value = "Welcome to the chat!" }
177
+ }
178
+ else {
179
+ db.add "chat_message" {
180
+ data = {
181
+ channel: "chat_room",
182
+ user_id: $auth.id,
183
+ message: $input.message,
184
+ timestamp: now
185
+ }
186
+ }
187
+ var $welcome { value = null }
188
+ }
189
+ }
190
+ }
191
+
192
+ response = {status: "ok", welcome: $welcome}
193
+
194
+ history = false
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Workspace Trigger
201
+
202
+ Executes in response to workspace branch lifecycle events.
203
+
204
+ ### Syntax
205
+
206
+ ```xs
207
+ workspace_trigger "<name>" {
208
+ actions = {branch_new: true, branch_merge: true, branch_live: true}
209
+ active = true
210
+ description = "Description of this trigger"
211
+ tags = ["tag1", "tag2"]
212
+
213
+ input {
214
+ // Define input parameters
215
+ }
216
+
217
+ stack {
218
+ // Logic to execute when triggered
219
+ }
220
+
221
+ history = 100
222
+ }
223
+ ```
224
+
225
+ ### Required Clauses
226
+
227
+ | Clause | Description |
228
+ |--------|-------------|
229
+ | `input` | Input parameter definitions |
230
+ | `stack` | Logic to execute when triggered |
231
+
232
+ ### Optional Clauses
233
+
234
+ | Clause | Type | Description |
235
+ |--------|------|-------------|
236
+ | `actions` | object | Which branch events trigger execution |
237
+ | `active` | boolean | Enable/disable the trigger |
238
+ | `description` | string | Human-readable description |
239
+ | `history` | integer/string | History retention setting |
240
+ | `tags` | array | Tags for organization |
241
+
242
+ ### Actions
243
+
244
+ | Action | Description |
245
+ |--------|-------------|
246
+ | `branch_new` | Trigger when a new branch is created |
247
+ | `branch_merge` | Trigger when a branch is merged |
248
+ | `branch_live` | Trigger when a branch goes live |
249
+
250
+ ### Example
251
+
252
+ ```xs
253
+ workspace_trigger "branch_notification" {
254
+ actions = {branch_new: true, branch_merge: true, branch_live: true}
255
+ active = true
256
+ description = "Send notifications on branch events"
257
+ tags = ["devops", "notifications"]
258
+
259
+ input {
260
+ }
261
+
262
+ stack {
263
+ util.send_email {
264
+ service_provider = "resend"
265
+ api_key = $env.RESEND_API_KEY
266
+ to = "team@example.com"
267
+ from = "system@example.com"
268
+ subject = "Branch Event: " ~ $trigger.action
269
+ message = "Branch '" ~ $trigger.branch_name ~ "' event: " ~ $trigger.action
270
+ }
271
+ }
272
+
273
+ history = false
274
+ }
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Agent Trigger
280
+
281
+ Handles connections and interactions with AI agents.
282
+
283
+ ### Syntax
284
+
285
+ ```xs
286
+ agent_trigger "<name>" {
287
+ agent = "<agent_name>"
288
+ actions = {connection: true}
289
+ active = true
290
+ description = "Description of this trigger"
291
+ docs = "Extended documentation for the trigger"
292
+ tags = ["tag1", "tag2"]
293
+
294
+ input {
295
+ // Define input parameters
296
+ }
297
+
298
+ stack {
299
+ // Logic to execute when triggered
300
+ }
301
+
302
+ response = $result
303
+
304
+ history = "inherit"
305
+ }
306
+ ```
307
+
308
+ ### Required Clauses
309
+
310
+ | Clause | Description |
311
+ |--------|-------------|
312
+ | `agent` | The AI agent name this trigger handles |
313
+ | `input` | Input parameter definitions |
314
+ | `stack` | Logic to execute when triggered |
315
+ | `response` | Value to return |
316
+
317
+ ### Optional Clauses
318
+
319
+ | Clause | Type | Description |
320
+ |--------|------|-------------|
321
+ | `actions` | object | Which agent events trigger execution |
322
+ | `active` | boolean | Enable/disable the trigger |
323
+ | `description` | string | Short description |
324
+ | `docs` | string | Extended documentation |
325
+ | `history` | integer/string/boolean | History retention setting |
326
+ | `tags` | array | Tags for organization |
327
+
328
+ ### Actions
329
+
330
+ | Action | Description |
331
+ |--------|-------------|
332
+ | `connection` | Trigger on agent connection events |
333
+
334
+ ### Example
335
+
336
+ ```xs
337
+ agent_trigger "assistant_handler" {
338
+ agent = "customer_assistant"
339
+ actions = {connection: true}
340
+ active = true
341
+ description = "Handle customer assistant agent connections"
342
+ docs = "This trigger initializes the customer context when the agent connects"
343
+
344
+ input {
345
+ text session_id filters=trim
346
+ }
347
+
348
+ stack {
349
+ db.get "customer" {
350
+ field_name = "session_id"
351
+ field_value = $input.session_id
352
+ } as $customer
353
+
354
+ var $context {
355
+ value = {
356
+ customer_name: $customer.name,
357
+ customer_tier: $customer.tier,
358
+ history: $customer.support_history
359
+ }
360
+ }
361
+ }
362
+
363
+ response = $context
364
+
365
+ history = "inherit"
366
+ }
367
+ ```
368
+
369
+ ---
370
+
371
+ ## MCP Server Trigger
372
+
373
+ Handles tool calls from MCP (Model Context Protocol) servers.
374
+
375
+ ### Syntax
376
+
377
+ ```xs
378
+ mcp_server_trigger "<name>" {
379
+ mcp_server = "<server_name>"
380
+ actions = {connection: true}
381
+ active = true
382
+ description = "Description of this trigger"
383
+ tags = ["tag1", "tag2"]
384
+
385
+ input {
386
+ // Define input parameters
387
+ }
388
+
389
+ stack {
390
+ // Logic to execute when triggered
391
+ }
392
+
393
+ response = $result
394
+
395
+ history = false
396
+ }
397
+ ```
398
+
399
+ ### Required Clauses
400
+
401
+ | Clause | Description |
402
+ |--------|-------------|
403
+ | `mcp_server` | The MCP server name this trigger handles |
404
+ | `input` | Input parameter definitions |
405
+ | `stack` | Logic to execute when triggered |
406
+ | `response` | Value to return to the MCP client |
407
+
408
+ ### Optional Clauses
409
+
410
+ | Clause | Type | Description |
411
+ |--------|------|-------------|
412
+ | `actions` | object | Which MCP events trigger execution |
413
+ | `active` | boolean | Enable/disable the trigger |
414
+ | `description` | string | Human-readable description |
415
+ | `history` | integer/string/boolean | History retention setting |
416
+ | `tags` | array | Tags for organization |
417
+
418
+ ### Actions
419
+
420
+ | Action | Description |
421
+ |--------|-------------|
422
+ | `connection` | Trigger on MCP connection events (required) |
423
+
424
+ ### Example
425
+
426
+ ```xs
427
+ mcp_server_trigger "database_tool_handler" {
428
+ mcp_server = "database_tools"
429
+ actions = {connection: true}
430
+ active = true
431
+ description = "Handle database tool calls from MCP clients"
432
+ tags = ["mcp", "database"]
433
+
434
+ input {
435
+ text operation filters=trim
436
+ object params
437
+ }
438
+
439
+ stack {
440
+ conditional {
441
+ if ($input.operation == "query") {
442
+ db.query $input.params.table {
443
+ where = $input.params.where
444
+ } as $result
445
+ }
446
+ elseif ($input.operation == "insert") {
447
+ db.add $input.params.table {
448
+ data = $input.params.data
449
+ } as $result
450
+ }
451
+ else {
452
+ var $result { value = {error: "Unknown operation"} }
453
+ }
454
+ }
455
+ }
456
+
457
+ response = $result
458
+
459
+ history = false
460
+ }
461
+ ```
462
+
463
+ ---
464
+
465
+ ## Common Patterns
466
+
467
+ ### Error Handling in Triggers
468
+
469
+ ```xs
470
+ table_trigger "safe_audit" {
471
+ table = "sensitive_data"
472
+ actions = {insert: true, update: true, delete: true, truncate: false}
473
+
474
+ input {
475
+ }
476
+
477
+ stack {
478
+ try_catch {
479
+ try {
480
+ db.add "audit_log" {
481
+ data = {
482
+ action: $trigger.action,
483
+ timestamp: now
484
+ }
485
+ }
486
+ }
487
+ catch {
488
+ debug.log { value = "Audit logging failed" }
489
+ }
490
+ }
491
+ }
492
+ }
493
+ ```
494
+
495
+ ### Conditional Trigger Logic
496
+
497
+ ```xs
498
+ table_trigger "conditional_notification" {
499
+ table = "order"
500
+ actions = {insert: true, update: false, delete: false, truncate: false}
501
+
502
+ input {
503
+ }
504
+
505
+ stack {
506
+ conditional {
507
+ if ($trigger.record.total > 1000) {
508
+ util.send_email {
509
+ service_provider = "resend"
510
+ api_key = $env.RESEND_API_KEY
511
+ to = "sales@example.com"
512
+ from = "system@example.com"
513
+ subject = "High Value Order"
514
+ message = "Order #" ~ $trigger.record.id ~ " for $" ~ $trigger.record.total
515
+ }
516
+ }
517
+ }
518
+ }
519
+ }
520
+ ```
521
+
522
+ ---
523
+
524
+ ## Best Practices
525
+
526
+ 1. **Use descriptive names** - Indicate the event and action: `user_audit_log`, `chat_message_handler`
527
+ 2. **Handle errors gracefully** - Use try_catch to prevent trigger failures from affecting the main operation
528
+ 3. **Keep triggers lightweight** - Offload heavy processing to functions or tasks
529
+ 4. **Set appropriate history** - Use `history = false` for high-frequency triggers to save storage
530
+ 5. **Use tags** - Organize triggers with meaningful tags for easier management
531
+ 6. **Document with description** - Always provide a description explaining the trigger's purpose
532
+ 7. **Test thoroughly** - Triggers execute automatically, so ensure they handle edge cases
@@ -68,8 +68,8 @@ Access inputs in stack: `$input.username`, `$input.age`
68
68
 
69
69
  ### text
70
70
  ```xs
71
- text name filters=trim|lower # With filters
72
- text bio? filters=max:500 # Optional, max 500 chars
71
+ text name filters=trim|lower // With filters
72
+ text bio? filters=max:500 // Optional, max 500 chars
73
73
  ```
74
74
 
75
75
  ### int / decimal
@@ -80,7 +80,7 @@ decimal price filters=min:0.01
80
80
 
81
81
  ### bool
82
82
  ```xs
83
- bool is_active?=true # Defaults to true
83
+ bool is_active?=true // Defaults to true
84
84
  bool confirmed?=false
85
85
  ```
86
86
 
@@ -93,25 +93,25 @@ email contact filters=trim|lower {
93
93
 
94
94
  ### password
95
95
  ```xs
96
- password secret filters=min:8 # Minimum 8 characters
96
+ password secret filters=min:8 // Minimum 8 characters
97
97
  ```
98
98
 
99
99
  ### timestamp / date
100
100
  ```xs
101
- timestamp created_at?=now # Defaults to current time
101
+ timestamp created_at?=now // Defaults to current time
102
102
  date birth_date
103
103
  ```
104
104
 
105
105
  ### uuid
106
106
  ```xs
107
107
  uuid session_id
108
- uuid user_id { table = "user" } # Foreign key reference
108
+ uuid user_id { table = "user" } // Foreign key reference
109
109
  ```
110
110
 
111
111
  ### json
112
112
  ```xs
113
- json metadata # Any JSON structure
114
- json settings?={} # Default empty object
113
+ json metadata // Any JSON structure
114
+ json settings?={} // Default empty object
115
115
  ```
116
116
 
117
117
  ### enum
@@ -140,8 +140,8 @@ object address {
140
140
 
141
141
  ### Arrays
142
142
  ```xs
143
- text[] tags filters=trim|lower # Array of trimmed lowercase strings
144
- int[1:10] scores filters=min:0|max:100 # 1-10 integers between 0-100
143
+ text[] tags filters=trim|lower // Array of trimmed lowercase strings
144
+ int[1:10] scores filters=min:0|max:100 // 1-10 integers between 0-100
145
145
  object[] items {
146
146
  schema {
147
147
  int id
@@ -212,19 +212,19 @@ input {
212
212
 
213
213
  ```xs
214
214
  input {
215
- # Required, cannot be null
215
+ // Required, cannot be null
216
216
  text required_field
217
217
 
218
- # Required, can be null (must provide, can send null)
218
+ // Required, can be null (must provide, can send null)
219
219
  text? nullable_field
220
220
 
221
- # Optional, cannot be null (can omit, but if sent must have value)
221
+ // Optional, cannot be null (can omit, but if sent must have value)
222
222
  text optional_field?
223
223
 
224
- # Optional, can be null (can omit or send null)
224
+ // Optional, can be null (can omit or send null)
225
225
  text? nullable_optional?
226
226
 
227
- # Optional with default
227
+ // Optional with default
228
228
  text with_default?="hello"
229
229
  }
230
230
  ```
@@ -268,19 +268,12 @@ input {
268
268
 
269
269
  ## Validation with Preconditions
270
270
 
271
- For complex validation beyond filters:
271
+ For complex validation beyond filters, use preconditions. For complete error handling reference, use `xanoscript_docs({ keyword: "syntax" })`.
272
272
 
273
273
  ```xs
274
- stack {
275
- precondition ($input.start_date < $input.end_date) {
276
- error_type = "inputerror"
277
- error = "Start date must be before end date"
278
- }
279
-
280
- precondition ($input.password == $input.confirm_password) {
281
- error_type = "inputerror"
282
- error = "Passwords must match"
283
- }
274
+ precondition ($input.start_date < $input.end_date) {
275
+ error_type = "inputerror"
276
+ error = "Start date must be before end date"
284
277
  }
285
278
  ```
286
279
 
@@ -290,8 +283,5 @@ stack {
290
283
 
291
284
  1. **Always specify types** - Never leave inputs untyped
292
285
  2. **Use filters first** - Prefer declarative filters over stack validation
293
- 3. **Add descriptions** - Document every field's purpose
294
- 4. **Mark sensitive data** - Use `sensitive = true` for PII/credentials
295
- 5. **Use defaults sparingly** - Make requirements explicit
296
- 6. **Validate at boundaries** - Validate user input, trust internal calls
297
- 7. **Limit nesting depth** - Keep object schemas 2-3 levels max
286
+ 3. **Mark sensitive data** - Use `sensitive = true` for PII/credentials
287
+ 4. **Validate at boundaries** - Validate user input, trust internal calls