@xano/developer-mcp 1.0.9 → 1.0.11

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 CHANGED
@@ -212,21 +212,17 @@ xanoscript_docs({ file_path: "apis/users/create.xs" })
212
212
  xanoscript_docs({ topic: "database", mode: "quick_reference" })
213
213
  ```
214
214
 
215
- ### 4. `init_workspace`
215
+ ### 4. `mcp_version`
216
216
 
217
- Get comprehensive instructions for initializing a local Xano development workspace.
217
+ Get the current version of the Xano Developer MCP server.
218
218
 
219
219
  **Parameters:** None
220
220
 
221
- **Returns:** Documentation on:
222
- - Directory structure for local development
223
- - File naming conventions
224
- - Registry format for tracking changes
225
- - Workflows for pulling/pushing XanoScript files via the Headless API
221
+ **Returns:** The version string from package.json.
226
222
 
227
223
  **Example:**
228
224
  ```
229
- init_workspace()
225
+ mcp_version()
230
226
  ```
231
227
 
232
228
  ## MCP Resources
@@ -276,7 +272,6 @@ xano-developer-mcp/
276
272
  │ ├── index.ts # Main MCP server implementation
277
273
  │ ├── xanoscript.d.ts # TypeScript declarations
278
274
  │ └── templates/
279
- │ ├── init-workspace.ts # Workspace initialization template
280
275
  │ └── xanoscript-index.ts
281
276
  ├── dist/ # Compiled JavaScript output
282
277
  ├── scripts/
@@ -322,7 +317,7 @@ Xano Developer MCP Server
322
317
 
323
318
  ├─► xanoscript_docs → Context-aware docs from /xanoscript_docs/*.md
324
319
 
325
- ├─► init_workspace → Returns workspace setup instructions
320
+ ├─► mcp_version → Returns server version from package.json
326
321
 
327
322
  └─► MCP Resources → Direct access to documentation files
328
323
  ```
package/dist/index.js CHANGED
@@ -8,7 +8,6 @@ import { dirname, join } from "path";
8
8
  import { minimatch } from "minimatch";
9
9
  import { xanoscriptParser } from "@xano/xanoscript-language-server/parser/parser.js";
10
10
  import { getSchemeFromContent } from "@xano/xanoscript-language-server/utils.js";
11
- import { generateInitWorkspaceTemplate } from "./templates/init-workspace.js";
12
11
  const __filename = fileURLToPath(import.meta.url);
13
12
  const __dirname = dirname(__filename);
14
13
  const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
@@ -134,69 +133,6 @@ const XANOSCRIPT_DOCS_V2 = {
134
133
  description: "Streaming data from files, requests, and responses",
135
134
  },
136
135
  };
137
- const XANO_OBJECT_TYPES = {
138
- function: {
139
- path: "functions",
140
- endpoint: "function",
141
- extension: ".xs",
142
- hasXanoscript: true,
143
- },
144
- table: {
145
- path: "tables",
146
- endpoint: "table",
147
- extension: ".xs",
148
- hasXanoscript: true,
149
- },
150
- task: {
151
- path: "tasks",
152
- endpoint: "task",
153
- extension: ".xs",
154
- hasXanoscript: true,
155
- },
156
- api_group: {
157
- path: "apis",
158
- endpoint: "api-group",
159
- extension: ".xs",
160
- hasXanoscript: true,
161
- supportsNesting: true,
162
- },
163
- tool: {
164
- path: "tools",
165
- endpoint: "tool",
166
- extension: ".xs",
167
- hasXanoscript: true,
168
- },
169
- agent: {
170
- path: "agents",
171
- endpoint: "agent",
172
- extension: ".xs",
173
- hasXanoscript: true,
174
- },
175
- middleware: {
176
- path: "middlewares",
177
- endpoint: "middleware",
178
- extension: ".xs",
179
- hasXanoscript: true,
180
- },
181
- addon: {
182
- path: "addons",
183
- endpoint: "addon",
184
- extension: ".xs",
185
- hasXanoscript: true,
186
- },
187
- mcp_server: {
188
- path: "mcp_servers",
189
- endpoint: "mcp-server",
190
- extension: ".xs",
191
- hasXanoscript: true,
192
- },
193
- realtime_channel: {
194
- path: "realtime",
195
- endpoint: "realtime-channel",
196
- extension: ".xs",
197
- hasXanoscript: true,
198
- },
199
- };
200
136
  // =============================================================================
201
137
  // API Documentation Configuration
202
138
  // =============================================================================
@@ -383,17 +319,6 @@ function readXanoscriptDocsV2(args) {
383
319
  }
384
320
  }
385
321
  // =============================================================================
386
- // Init Workspace Documentation
387
- // =============================================================================
388
- function generateInitWorkspaceDoc() {
389
- const objectTypes = Object.entries(XANO_OBJECT_TYPES).map(([type, config]) => ({
390
- type,
391
- path: config.path,
392
- endpoint: config.endpoint,
393
- }));
394
- return generateInitWorkspaceTemplate(objectTypes);
395
- }
396
- // =============================================================================
397
322
  // MCP Server Setup
398
323
  // =============================================================================
399
324
  const server = new Server({
@@ -507,11 +432,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
507
432
  },
508
433
  },
509
434
  {
510
- name: "init_workspace",
511
- description: "Get comprehensive instructions for initializing a local Xano development workspace. " +
512
- "Returns documentation on directory structure, file naming conventions, registry format for tracking changes, " +
513
- "and workflows for pulling/pushing XanoScript files via the Headless API. " +
514
- "Use this when setting up local development for Xano projects.",
435
+ name: "mcp_version",
436
+ description: "Get the current version of the Xano Developer MCP server. " +
437
+ "Returns the version string from package.json.",
515
438
  inputSchema: {
516
439
  type: "object",
517
440
  properties: {},
@@ -619,13 +542,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
619
542
  ],
620
543
  };
621
544
  }
622
- if (request.params.name === "init_workspace") {
623
- const documentation = generateInitWorkspaceDoc();
545
+ if (request.params.name === "mcp_version") {
624
546
  return {
625
547
  content: [
626
548
  {
627
549
  type: "text",
628
- text: documentation,
550
+ text: SERVER_VERSION,
629
551
  },
630
552
  ],
631
553
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/developer-mcp",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "MCP server for Xano Headless API documentation and XanoScript code validation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,15 +9,16 @@ Reusable subqueries for fetching related data in database queries.
9
9
  ## Quick Reference
10
10
 
11
11
  ```xs
12
- addon "<name>" {
13
- description = "What this addon fetches"
12
+ addon <name> {
14
13
  input {
15
14
  <type> <name>
16
15
  }
16
+
17
17
  stack {
18
- // Query logic
18
+ db.query <tableName> {
19
+ return = {type: "<return_type>"}
20
+ }
19
21
  }
20
- response = $result
21
22
  }
22
23
  ```
23
24
 
@@ -25,21 +26,20 @@ addon "<name>" {
25
26
 
26
27
  ## Basic Structure
27
28
 
28
- Addons define reusable data fetching logic that can be attached to query results.
29
+ Addons define reusable data fetching logic that can be attached to query results. The stack can only contain a `db.query` block.
29
30
 
30
31
  ```xs
31
- addon "comment_count" {
32
- description = "Count of comments for a post"
32
+ addon comment_count {
33
33
  input {
34
34
  int post_id
35
35
  }
36
+
36
37
  stack {
37
- db.query "comment" {
38
+ db.query comment {
38
39
  where = $db.comment.post_id == $input.post_id
39
- return = { type: "count" }
40
- } as $count
40
+ return = {type: "count"}
41
+ }
41
42
  }
42
- response = $count
43
43
  }
44
44
  ```
45
45
 
@@ -50,12 +50,12 @@ addon "comment_count" {
50
50
  ### In db.query
51
51
 
52
52
  ```xs
53
- db.query "post" {
53
+ db.query post {
54
54
  where = $db.post.author_id == $auth.id
55
55
  addon = [
56
56
  {
57
57
  name: "comment_count",
58
- input: { post_id: $output.id },
58
+ input: {post_id: $output.id},
59
59
  as: "items.comment_count"
60
60
  }
61
61
  ]
@@ -79,22 +79,22 @@ db.query "post" {
79
79
  ### Multiple Addons
80
80
 
81
81
  ```xs
82
- db.query "post" {
82
+ db.query post {
83
83
  where = $db.post.is_published == true
84
84
  addon = [
85
85
  {
86
86
  name: "comment_count",
87
- input: { post_id: $output.id },
87
+ input: {post_id: $output.id},
88
88
  as: "items.comments"
89
89
  },
90
90
  {
91
91
  name: "like_count",
92
- input: { post_id: $output.id },
92
+ input: {post_id: $output.id},
93
93
  as: "items.likes"
94
94
  },
95
95
  {
96
96
  name: "author_details",
97
- input: { user_id: $output.author_id },
97
+ input: {user_id: $output.author_id},
98
98
  as: "items.author"
99
99
  }
100
100
  ]
@@ -108,103 +108,75 @@ db.query "post" {
108
108
  ### Related List
109
109
 
110
110
  ```xs
111
- addon "recent_comments" {
112
- description = "Get recent comments for a post"
111
+ addon recent_comments {
113
112
  input {
114
113
  int post_id
115
114
  int limit?=5
116
115
  }
116
+
117
117
  stack {
118
- db.query "comment" {
118
+ db.query comment {
119
119
  where = $db.comment.post_id == $input.post_id
120
- sort = { created_at: "desc" }
120
+ sort = {created_at: "desc"}
121
121
  return = {
122
122
  type: "list",
123
- paging: { per_page: $input.limit }
123
+ paging: {per_page: $input.limit}
124
124
  }
125
- } as $comments
125
+ }
126
126
  }
127
- response = $comments.items
128
127
  }
129
128
  ```
130
129
 
131
- ### Aggregation
130
+ ### Count
132
131
 
133
132
  ```xs
134
- addon "order_stats" {
135
- description = "Order statistics for a user"
133
+ addon order_count {
136
134
  input {
137
135
  int user_id
138
136
  }
137
+
139
138
  stack {
140
- db.query "order" {
139
+ db.query order {
141
140
  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
- }
141
+ return = {type: "count"}
150
142
  }
151
143
  }
152
- response = $stats
153
144
  }
154
145
  ```
155
146
 
156
- ### Nested Object
147
+ ### Boolean Check (Exists)
157
148
 
158
149
  ```xs
159
- addon "full_address" {
160
- description = "Get formatted address for a user"
150
+ addon has_premium {
161
151
  input {
162
152
  int user_id
163
153
  }
154
+
164
155
  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
- }
156
+ db.query subscription {
157
+ where = $db.subscription.user_id == $input.user_id
158
+ && $db.subscription.status == "active"
159
+ && $db.subscription.expires_at > now
160
+ return = {type: "exists"}
185
161
  }
186
162
  }
187
- response = $formatted
188
163
  }
189
164
  ```
190
165
 
191
- ### Boolean Check
166
+ ### Single Record
192
167
 
193
168
  ```xs
194
- addon "has_premium" {
195
- description = "Check if user has premium subscription"
169
+ addon author_details {
196
170
  input {
197
171
  int user_id
198
172
  }
173
+
199
174
  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
175
+ db.query user {
176
+ where = $db.user.id == $input.user_id
177
+ return = {type: "single"}
178
+ }
206
179
  }
207
- response = $has_premium
208
180
  }
209
181
  ```
210
182
 
@@ -215,8 +187,8 @@ addon "has_premium" {
215
187
  Call an addon directly from a function or API.
216
188
 
217
189
  ```xs
218
- addon.call "comment_count" {
219
- input = { post_id: $input.post_id }
190
+ addon.call comment_count {
191
+ input = {post_id: $input.post_id}
220
192
  } as $count
221
193
  ```
222
194
 
@@ -224,16 +196,16 @@ addon.call "comment_count" {
224
196
 
225
197
  ```xs
226
198
  // Get stats for single record
227
- addon.call "order_stats" {
228
- input = { user_id: $auth.id }
229
- } as $my_stats
199
+ addon.call order_count {
200
+ input = {user_id: $auth.id}
201
+ } as $my_order_count
230
202
 
231
203
  // Conditional addon call
232
204
  conditional {
233
205
  if ($input.include_stats) {
234
- addon.call "detailed_stats" {
235
- input = { entity_id: $entity.id }
236
- } as $stats
206
+ addon.call order_count {
207
+ input = {user_id: $entity.id}
208
+ } as $count
237
209
  }
238
210
  }
239
211
  ```
@@ -245,9 +217,9 @@ conditional {
245
217
  Use `dbAddonAttr` for computed fields in queries.
246
218
 
247
219
  ```xs
248
- db.query "product" {
220
+ db.query product {
249
221
  eval = {
250
- discount_price: dbAddonAttr("calculate_discount", { product_id: $db.product.id })
222
+ discount_price: dbAddonAttr("calculate_discount", {product_id: $db.product.id})
251
223
  }
252
224
  } as $products
253
225
  ```
@@ -263,7 +235,7 @@ addons/
263
235
  ├── comment_count.xs
264
236
  ├── like_count.xs
265
237
  ├── author_details.xs
266
- ├── order_stats.xs
238
+ ├── order_count.xs
267
239
  └── has_premium.xs
268
240
  ```
269
241
 
@@ -279,7 +251,6 @@ addons/
279
251
 
280
252
  1. **Keep addons focused** - One purpose per addon
281
253
  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
254
+ 3. **Use appropriate return types** - `list`, `single`, `count`, `exists`
255
+ 4. **Limit nested queries** - Avoid N+1 query patterns
256
+ 5. **Document inputs** - Add descriptions to input fields