@xano/developer-mcp 1.0.7 → 1.0.9
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 +28 -0
- package/dist/index.js +42 -1
- package/dist/templates/xanoscript-index.js +7 -0
- package/package.json +1 -1
- package/xanoscript_docs/README.md +38 -5
- package/xanoscript_docs/addons.md +285 -0
- package/xanoscript_docs/agents.md +84 -0
- package/xanoscript_docs/database.md +160 -0
- package/xanoscript_docs/debugging.md +342 -0
- package/xanoscript_docs/functions.md +172 -0
- package/xanoscript_docs/integrations.md +376 -7
- package/xanoscript_docs/performance.md +407 -0
- package/xanoscript_docs/realtime.md +382 -0
- package/xanoscript_docs/schema.md +292 -0
- package/xanoscript_docs/security.md +550 -0
- package/xanoscript_docs/streaming.md +318 -0
- package/xanoscript_docs/syntax.md +267 -0
- package/xanoscript_docs/triggers.md +354 -52
- package/xanoscript_docs/types.md +66 -21
package/README.md
CHANGED
|
@@ -39,6 +39,18 @@ Add to your Claude Desktop configuration file:
|
|
|
39
39
|
}
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Checking Your Version
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx @xano/developer-mcp --version
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If installed from source:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
node dist/index.js --version
|
|
52
|
+
```
|
|
53
|
+
|
|
42
54
|
## Installation from Source
|
|
43
55
|
|
|
44
56
|
### Prerequisites
|
|
@@ -168,6 +180,7 @@ Retrieves XanoScript programming language documentation with context-aware suppo
|
|
|
168
180
|
| `functions` | Reusable function stacks with inputs and responses |
|
|
169
181
|
| `apis` | HTTP endpoint definitions with authentication and CRUD patterns |
|
|
170
182
|
| `tasks` | Scheduled and cron jobs |
|
|
183
|
+
| `triggers` | Event-driven handlers (table, realtime, workspace, agent, MCP) |
|
|
171
184
|
| `database` | All db.* operations: query, get, add, edit, patch, delete |
|
|
172
185
|
| `agents` | AI agent configuration with LLM providers and tools |
|
|
173
186
|
| `tools` | AI tools for agents and MCP servers |
|
|
@@ -176,6 +189,13 @@ Retrieves XanoScript programming language documentation with context-aware suppo
|
|
|
176
189
|
| `integrations` | Cloud storage, Redis, security, and external APIs |
|
|
177
190
|
| `frontend` | Static frontend development and deployment |
|
|
178
191
|
| `ephemeral` | Temporary test environments |
|
|
192
|
+
| `addons` | Reusable subqueries for fetching related data |
|
|
193
|
+
| `debugging` | Logging, inspecting, and debugging XanoScript execution |
|
|
194
|
+
| `performance` | Performance optimization best practices |
|
|
195
|
+
| `realtime` | Real-time channels and events for push updates |
|
|
196
|
+
| `schema` | Runtime schema parsing and validation |
|
|
197
|
+
| `security` | Security best practices for authentication and authorization |
|
|
198
|
+
| `streaming` | Streaming data from files, requests, and responses |
|
|
179
199
|
|
|
180
200
|
**Examples:**
|
|
181
201
|
```
|
|
@@ -222,6 +242,7 @@ The server also exposes XanoScript documentation as MCP resources for direct acc
|
|
|
222
242
|
| `xanoscript://docs/functions` | Reusable function stacks |
|
|
223
243
|
| `xanoscript://docs/apis` | HTTP endpoint definitions |
|
|
224
244
|
| `xanoscript://docs/tasks` | Scheduled and cron jobs |
|
|
245
|
+
| `xanoscript://docs/triggers` | Event-driven handlers |
|
|
225
246
|
| `xanoscript://docs/database` | Database operations |
|
|
226
247
|
| `xanoscript://docs/agents` | AI agent configuration |
|
|
227
248
|
| `xanoscript://docs/tools` | AI tools for agents |
|
|
@@ -230,6 +251,13 @@ The server also exposes XanoScript documentation as MCP resources for direct acc
|
|
|
230
251
|
| `xanoscript://docs/integrations` | External service integrations |
|
|
231
252
|
| `xanoscript://docs/frontend` | Static frontend development |
|
|
232
253
|
| `xanoscript://docs/ephemeral` | Temporary test environments |
|
|
254
|
+
| `xanoscript://docs/addons` | Reusable subqueries for related data |
|
|
255
|
+
| `xanoscript://docs/debugging` | Logging and debugging tools |
|
|
256
|
+
| `xanoscript://docs/performance` | Performance optimization |
|
|
257
|
+
| `xanoscript://docs/realtime` | Real-time channels and events |
|
|
258
|
+
| `xanoscript://docs/schema` | Runtime schema parsing |
|
|
259
|
+
| `xanoscript://docs/security` | Security best practices |
|
|
260
|
+
| `xanoscript://docs/streaming` | Data streaming operations |
|
|
233
261
|
|
|
234
262
|
## npm Scripts
|
|
235
263
|
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,12 @@ import { getSchemeFromContent } from "@xano/xanoscript-language-server/utils.js"
|
|
|
11
11
|
import { generateInitWorkspaceTemplate } from "./templates/init-workspace.js";
|
|
12
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
13
|
const __dirname = dirname(__filename);
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
15
|
+
const SERVER_VERSION = pkg.version;
|
|
16
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
17
|
+
console.log(SERVER_VERSION);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
14
20
|
const XANOSCRIPT_DOCS_V2 = {
|
|
15
21
|
readme: {
|
|
16
22
|
file: "README.md",
|
|
@@ -92,6 +98,41 @@ const XANOSCRIPT_DOCS_V2 = {
|
|
|
92
98
|
applyTo: ["ephemeral/**/*.xs"],
|
|
93
99
|
description: "Temporary test environments",
|
|
94
100
|
},
|
|
101
|
+
addons: {
|
|
102
|
+
file: "addons.md",
|
|
103
|
+
applyTo: ["addons/*.xs", "functions/**/*.xs", "apis/**/*.xs"],
|
|
104
|
+
description: "Reusable subqueries for fetching related data",
|
|
105
|
+
},
|
|
106
|
+
debugging: {
|
|
107
|
+
file: "debugging.md",
|
|
108
|
+
applyTo: ["**/*.xs"],
|
|
109
|
+
description: "Logging, inspecting, and debugging XanoScript execution",
|
|
110
|
+
},
|
|
111
|
+
performance: {
|
|
112
|
+
file: "performance.md",
|
|
113
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
114
|
+
description: "Performance optimization best practices",
|
|
115
|
+
},
|
|
116
|
+
realtime: {
|
|
117
|
+
file: "realtime.md",
|
|
118
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "triggers/**/*.xs"],
|
|
119
|
+
description: "Real-time channels and events for push updates",
|
|
120
|
+
},
|
|
121
|
+
schema: {
|
|
122
|
+
file: "schema.md",
|
|
123
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
124
|
+
description: "Runtime schema parsing and validation",
|
|
125
|
+
},
|
|
126
|
+
security: {
|
|
127
|
+
file: "security.md",
|
|
128
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
129
|
+
description: "Security best practices for authentication and authorization",
|
|
130
|
+
},
|
|
131
|
+
streaming: {
|
|
132
|
+
file: "streaming.md",
|
|
133
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
134
|
+
description: "Streaming data from files, requests, and responses",
|
|
135
|
+
},
|
|
95
136
|
};
|
|
96
137
|
const XANO_OBJECT_TYPES = {
|
|
97
138
|
function: {
|
|
@@ -357,7 +398,7 @@ function generateInitWorkspaceDoc() {
|
|
|
357
398
|
// =============================================================================
|
|
358
399
|
const server = new Server({
|
|
359
400
|
name: "xano-developer-mcp",
|
|
360
|
-
version:
|
|
401
|
+
version: SERVER_VERSION,
|
|
361
402
|
description: "MCP server for Xano Headless API documentation and XanoScript code validation",
|
|
362
403
|
}, {
|
|
363
404
|
capabilities: {
|
|
@@ -53,8 +53,15 @@ ${formatRow("task_workflow", "AI workflow for creating tasks")}
|
|
|
53
53
|
|
|
54
54
|
| Keyword | Aliases | Description |
|
|
55
55
|
|---------|---------|-------------|
|
|
56
|
+
${formatRow("addons", "Reusable subqueries for related data")}
|
|
57
|
+
${formatRow("debugging", "Logging and debugging tools")}
|
|
56
58
|
${formatRow("frontend", "Frontend development with Xano")}
|
|
57
59
|
${formatRow("lovable", "Building from Lovable-generated websites")}
|
|
60
|
+
${formatRow("performance", "Performance optimization best practices")}
|
|
61
|
+
${formatRow("realtime", "Real-time channels and events")}
|
|
62
|
+
${formatRow("schema", "Runtime schema parsing and validation")}
|
|
63
|
+
${formatRow("security", "Security best practices")}
|
|
64
|
+
${formatRow("streaming", "Streaming data from files and responses")}
|
|
58
65
|
${formatRow("testing", "Unit testing XanoScript code")}
|
|
59
66
|
${formatRow("tips", "Tips and tricks")}
|
|
60
67
|
${formatRow("ephemeral", "Ephemeral environment setup")}
|
package/package.json
CHANGED
|
@@ -105,19 +105,52 @@ This helps AI tools apply the correct documentation based on the file being edit
|
|
|
105
105
|
|
|
106
106
|
Use `xanoscript_docs({ keyword: "<keyword>" })` to retrieve documentation.
|
|
107
107
|
|
|
108
|
+
### Core Language
|
|
108
109
|
| Topic | Keyword | Description |
|
|
109
110
|
|-------|---------|-------------|
|
|
110
|
-
| Syntax Reference | `syntax` | Expressions, operators, filters |
|
|
111
|
+
| Syntax Reference | `syntax` | Expressions, operators, filters, system variables |
|
|
111
112
|
| Types & Inputs | `input` | Data types, validation, input blocks |
|
|
113
|
+
| Functions | `function` | Reusable function stacks, async, loops |
|
|
114
|
+
| Schema | `schema` | Runtime schema parsing and validation |
|
|
115
|
+
|
|
116
|
+
### Data
|
|
117
|
+
| Topic | Keyword | Description |
|
|
118
|
+
|-------|---------|-------------|
|
|
112
119
|
| Tables | `table` | Database schema definitions |
|
|
113
|
-
|
|
|
120
|
+
| Database Operations | `db_query` | Query, add, edit, delete, bulk operations |
|
|
121
|
+
| Addons | `addon` | Reusable subqueries for related data |
|
|
122
|
+
| Streaming | `streaming` | Stream processing for large files |
|
|
123
|
+
|
|
124
|
+
### APIs & Endpoints
|
|
125
|
+
| Topic | Keyword | Description |
|
|
126
|
+
|-------|---------|-------------|
|
|
114
127
|
| APIs | `api_query` | HTTP endpoint definitions |
|
|
115
128
|
| Tasks | `task` | Scheduled jobs |
|
|
116
|
-
| Triggers | `trigger` | Event-driven handlers
|
|
117
|
-
|
|
|
129
|
+
| Triggers | `trigger` | Event-driven handlers |
|
|
130
|
+
| Realtime | `realtime` | Push events and channels |
|
|
131
|
+
|
|
132
|
+
### AI & Agents
|
|
133
|
+
| Topic | Keyword | Description |
|
|
134
|
+
|-------|---------|-------------|
|
|
118
135
|
| Agents | `agent` | AI agent configuration |
|
|
119
136
|
| Tools | `tool` | AI tools for agents |
|
|
120
137
|
| MCP Servers | `mcp_server` | Model Context Protocol servers |
|
|
138
|
+
|
|
139
|
+
### Integrations
|
|
140
|
+
| Topic | Keyword | Description |
|
|
141
|
+
|-------|---------|-------------|
|
|
142
|
+
| Integrations | `integrations` | Cloud storage, search, Redis, zip, Lambda |
|
|
143
|
+
|
|
144
|
+
### Development
|
|
145
|
+
| Topic | Keyword | Description |
|
|
146
|
+
|-------|---------|-------------|
|
|
121
147
|
| Testing | `testing` | Unit tests and mocking |
|
|
148
|
+
| Debugging | `debugging` | Logging and inspection tools |
|
|
122
149
|
| Frontend | `frontend` | Static frontend development |
|
|
123
|
-
| Ephemeral | `ephemeral` | Temporary environments
|
|
150
|
+
| Ephemeral | `ephemeral` | Temporary environments |
|
|
151
|
+
|
|
152
|
+
### Best Practices
|
|
153
|
+
| Topic | Keyword | Description |
|
|
154
|
+
|-------|---------|-------------|
|
|
155
|
+
| Performance | `performance` | Query optimization, caching, parallelism |
|
|
156
|
+
| Security | `security` | Authentication, authorization, encryption |
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "addons/*.xs, functions/**/*.xs, apis/**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Addons
|
|
6
|
+
|
|
7
|
+
Reusable subqueries for fetching related data in database queries.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
```xs
|
|
12
|
+
addon "<name>" {
|
|
13
|
+
description = "What this addon fetches"
|
|
14
|
+
input {
|
|
15
|
+
<type> <name>
|
|
16
|
+
}
|
|
17
|
+
stack {
|
|
18
|
+
// Query logic
|
|
19
|
+
}
|
|
20
|
+
response = $result
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Basic Structure
|
|
27
|
+
|
|
28
|
+
Addons define reusable data fetching logic that can be attached to query results.
|
|
29
|
+
|
|
30
|
+
```xs
|
|
31
|
+
addon "comment_count" {
|
|
32
|
+
description = "Count of comments for a post"
|
|
33
|
+
input {
|
|
34
|
+
int post_id
|
|
35
|
+
}
|
|
36
|
+
stack {
|
|
37
|
+
db.query "comment" {
|
|
38
|
+
where = $db.comment.post_id == $input.post_id
|
|
39
|
+
return = { type: "count" }
|
|
40
|
+
} as $count
|
|
41
|
+
}
|
|
42
|
+
response = $count
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Using Addons in Queries
|
|
49
|
+
|
|
50
|
+
### In db.query
|
|
51
|
+
|
|
52
|
+
```xs
|
|
53
|
+
db.query "post" {
|
|
54
|
+
where = $db.post.author_id == $auth.id
|
|
55
|
+
addon = [
|
|
56
|
+
{
|
|
57
|
+
name: "comment_count",
|
|
58
|
+
input: { post_id: $output.id },
|
|
59
|
+
as: "items.comment_count"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
} as $posts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Result Structure
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"items": [
|
|
70
|
+
{
|
|
71
|
+
"id": 1,
|
|
72
|
+
"title": "My Post",
|
|
73
|
+
"comment_count": 42
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Multiple Addons
|
|
80
|
+
|
|
81
|
+
```xs
|
|
82
|
+
db.query "post" {
|
|
83
|
+
where = $db.post.is_published == true
|
|
84
|
+
addon = [
|
|
85
|
+
{
|
|
86
|
+
name: "comment_count",
|
|
87
|
+
input: { post_id: $output.id },
|
|
88
|
+
as: "items.comments"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "like_count",
|
|
92
|
+
input: { post_id: $output.id },
|
|
93
|
+
as: "items.likes"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "author_details",
|
|
97
|
+
input: { user_id: $output.author_id },
|
|
98
|
+
as: "items.author"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
} as $posts
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Addon Examples
|
|
107
|
+
|
|
108
|
+
### Related List
|
|
109
|
+
|
|
110
|
+
```xs
|
|
111
|
+
addon "recent_comments" {
|
|
112
|
+
description = "Get recent comments for a post"
|
|
113
|
+
input {
|
|
114
|
+
int post_id
|
|
115
|
+
int limit?=5
|
|
116
|
+
}
|
|
117
|
+
stack {
|
|
118
|
+
db.query "comment" {
|
|
119
|
+
where = $db.comment.post_id == $input.post_id
|
|
120
|
+
sort = { created_at: "desc" }
|
|
121
|
+
return = {
|
|
122
|
+
type: "list",
|
|
123
|
+
paging: { per_page: $input.limit }
|
|
124
|
+
}
|
|
125
|
+
} as $comments
|
|
126
|
+
}
|
|
127
|
+
response = $comments.items
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Aggregation
|
|
132
|
+
|
|
133
|
+
```xs
|
|
134
|
+
addon "order_stats" {
|
|
135
|
+
description = "Order statistics for a user"
|
|
136
|
+
input {
|
|
137
|
+
int user_id
|
|
138
|
+
}
|
|
139
|
+
stack {
|
|
140
|
+
db.query "order" {
|
|
141
|
+
where = $db.order.user_id == $input.user_id
|
|
142
|
+
} as $orders
|
|
143
|
+
|
|
144
|
+
var $stats {
|
|
145
|
+
value = {
|
|
146
|
+
total_orders: $orders|count,
|
|
147
|
+
total_spent: $orders|map:$$.total|sum,
|
|
148
|
+
avg_order: $orders|map:$$.total|avg
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
response = $stats
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Nested Object
|
|
157
|
+
|
|
158
|
+
```xs
|
|
159
|
+
addon "full_address" {
|
|
160
|
+
description = "Get formatted address for a user"
|
|
161
|
+
input {
|
|
162
|
+
int user_id
|
|
163
|
+
}
|
|
164
|
+
stack {
|
|
165
|
+
db.get "address" {
|
|
166
|
+
field_name = "user_id"
|
|
167
|
+
field_value = $input.user_id
|
|
168
|
+
} as $address
|
|
169
|
+
|
|
170
|
+
conditional {
|
|
171
|
+
if ($address != null) {
|
|
172
|
+
var $formatted {
|
|
173
|
+
value = {
|
|
174
|
+
street: $address.street,
|
|
175
|
+
city: $address.city,
|
|
176
|
+
state: $address.state,
|
|
177
|
+
zip: $address.zip,
|
|
178
|
+
full: $address.street ~ ", " ~ $address.city ~ ", " ~ $address.state ~ " " ~ $address.zip
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
var $formatted { value = null }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
response = $formatted
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Boolean Check
|
|
192
|
+
|
|
193
|
+
```xs
|
|
194
|
+
addon "has_premium" {
|
|
195
|
+
description = "Check if user has premium subscription"
|
|
196
|
+
input {
|
|
197
|
+
int user_id
|
|
198
|
+
}
|
|
199
|
+
stack {
|
|
200
|
+
db.query "subscription" {
|
|
201
|
+
where = $db.subscription.user_id == $input.user_id
|
|
202
|
+
&& $db.subscription.status == "active"
|
|
203
|
+
&& $db.subscription.expires_at > now
|
|
204
|
+
return = { type: "exists" }
|
|
205
|
+
} as $has_premium
|
|
206
|
+
}
|
|
207
|
+
response = $has_premium
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## addon.call
|
|
214
|
+
|
|
215
|
+
Call an addon directly from a function or API.
|
|
216
|
+
|
|
217
|
+
```xs
|
|
218
|
+
addon.call "comment_count" {
|
|
219
|
+
input = { post_id: $input.post_id }
|
|
220
|
+
} as $count
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Use Cases
|
|
224
|
+
|
|
225
|
+
```xs
|
|
226
|
+
// Get stats for single record
|
|
227
|
+
addon.call "order_stats" {
|
|
228
|
+
input = { user_id: $auth.id }
|
|
229
|
+
} as $my_stats
|
|
230
|
+
|
|
231
|
+
// Conditional addon call
|
|
232
|
+
conditional {
|
|
233
|
+
if ($input.include_stats) {
|
|
234
|
+
addon.call "detailed_stats" {
|
|
235
|
+
input = { entity_id: $entity.id }
|
|
236
|
+
} as $stats
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Database Addon Attributes
|
|
244
|
+
|
|
245
|
+
Use `dbAddonAttr` for computed fields in queries.
|
|
246
|
+
|
|
247
|
+
```xs
|
|
248
|
+
db.query "product" {
|
|
249
|
+
eval = {
|
|
250
|
+
discount_price: dbAddonAttr("calculate_discount", { product_id: $db.product.id })
|
|
251
|
+
}
|
|
252
|
+
} as $products
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Organization
|
|
258
|
+
|
|
259
|
+
### File Structure
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
addons/
|
|
263
|
+
├── comment_count.xs
|
|
264
|
+
├── like_count.xs
|
|
265
|
+
├── author_details.xs
|
|
266
|
+
├── order_stats.xs
|
|
267
|
+
└── has_premium.xs
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Naming Conventions
|
|
271
|
+
|
|
272
|
+
- Use descriptive names: `comment_count`, not `cc`
|
|
273
|
+
- Use verb_noun for actions: `calculate_discount`
|
|
274
|
+
- Use has_ prefix for boolean checks: `has_premium`
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Best Practices
|
|
279
|
+
|
|
280
|
+
1. **Keep addons focused** - One purpose per addon
|
|
281
|
+
2. **Use input parameters** - Make addons reusable
|
|
282
|
+
3. **Handle null cases** - Return sensible defaults
|
|
283
|
+
4. **Cache expensive addons** - Use Redis for slow queries
|
|
284
|
+
5. **Limit nested queries** - Avoid N+1 query patterns
|
|
285
|
+
6. **Document inputs** - Add descriptions to input fields
|
|
@@ -317,6 +317,89 @@ agent "Research Assistant" {
|
|
|
317
317
|
|
|
318
318
|
---
|
|
319
319
|
|
|
320
|
+
## External MCP Tools
|
|
321
|
+
|
|
322
|
+
Integrate external MCP (Model Context Protocol) servers to extend agent capabilities.
|
|
323
|
+
|
|
324
|
+
### ai.external.mcp.tool.list
|
|
325
|
+
|
|
326
|
+
List available tools from an external MCP server.
|
|
327
|
+
|
|
328
|
+
```xs
|
|
329
|
+
ai.external.mcp.tool.list {
|
|
330
|
+
server_url = "https://mcp.example.com"
|
|
331
|
+
api_key = $env.MCP_API_KEY
|
|
332
|
+
} as $tools
|
|
333
|
+
|
|
334
|
+
// $tools = [
|
|
335
|
+
// { name: "search_web", description: "Search the web", inputSchema: {...} },
|
|
336
|
+
// { name: "fetch_page", description: "Fetch webpage content", inputSchema: {...} }
|
|
337
|
+
// ]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### ai.external.mcp.tool.run
|
|
341
|
+
|
|
342
|
+
Execute a tool on an external MCP server.
|
|
343
|
+
|
|
344
|
+
```xs
|
|
345
|
+
ai.external.mcp.tool.run {
|
|
346
|
+
server_url = "https://mcp.example.com"
|
|
347
|
+
api_key = $env.MCP_API_KEY
|
|
348
|
+
tool_name = "search_web"
|
|
349
|
+
arguments = { query: "latest AI news" }
|
|
350
|
+
} as $result
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### ai.external.mcp.server_details
|
|
354
|
+
|
|
355
|
+
Get metadata about an external MCP server.
|
|
356
|
+
|
|
357
|
+
```xs
|
|
358
|
+
ai.external.mcp.server_details {
|
|
359
|
+
server_url = "https://mcp.example.com"
|
|
360
|
+
api_key = $env.MCP_API_KEY
|
|
361
|
+
} as $details
|
|
362
|
+
|
|
363
|
+
// $details = {
|
|
364
|
+
// name: "Web Tools",
|
|
365
|
+
// version: "1.0.0",
|
|
366
|
+
// capabilities: ["tools", "resources"],
|
|
367
|
+
// tools: [...]
|
|
368
|
+
// }
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Using External Tools in Agents
|
|
372
|
+
|
|
373
|
+
```xs
|
|
374
|
+
agent "Research Agent" {
|
|
375
|
+
canonical = "research-v1"
|
|
376
|
+
llm = {
|
|
377
|
+
type: "openai"
|
|
378
|
+
api_key: "{{ $env.OPENAI_API_KEY }}"
|
|
379
|
+
model: "gpt-5"
|
|
380
|
+
system_prompt: """
|
|
381
|
+
You are a research assistant with access to web search
|
|
382
|
+
and document analysis tools. Use them to find and analyze information.
|
|
383
|
+
"""
|
|
384
|
+
prompt: "{{ $args.query }}"
|
|
385
|
+
max_steps: 8
|
|
386
|
+
}
|
|
387
|
+
tools = [
|
|
388
|
+
{ name: "search_web" },
|
|
389
|
+
{ name: "fetch_page" },
|
|
390
|
+
{ name: "summarize_document" }
|
|
391
|
+
]
|
|
392
|
+
external_mcp_servers = [
|
|
393
|
+
{
|
|
394
|
+
url: "{{ $env.WEB_MCP_URL }}",
|
|
395
|
+
api_key: "{{ $env.WEB_MCP_KEY }}"
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
320
403
|
## Best Practices
|
|
321
404
|
|
|
322
405
|
1. **Clear system prompts** - Define persona, capabilities, and constraints
|
|
@@ -325,3 +408,4 @@ agent "Research Assistant" {
|
|
|
325
408
|
4. **Don't repeat tool descriptions** - They're auto-injected
|
|
326
409
|
5. **Use environment variables** - Never hardcode API keys
|
|
327
410
|
6. **Test with xano-free first** - Free for development
|
|
411
|
+
7. **Validate external MCP servers** - Check server_details before using
|