@xano/developer-mcp 1.0.0

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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +261 -0
  3. package/api_docs/addon.md +193 -0
  4. package/api_docs/agent.md +154 -0
  5. package/api_docs/api_group.md +236 -0
  6. package/api_docs/authentication.md +68 -0
  7. package/api_docs/file.md +190 -0
  8. package/api_docs/function.md +217 -0
  9. package/api_docs/history.md +263 -0
  10. package/api_docs/index.md +104 -0
  11. package/api_docs/mcp_server.md +139 -0
  12. package/api_docs/middleware.md +205 -0
  13. package/api_docs/realtime.md +153 -0
  14. package/api_docs/table.md +151 -0
  15. package/api_docs/task.md +191 -0
  16. package/api_docs/tool.md +216 -0
  17. package/api_docs/triggers.md +344 -0
  18. package/api_docs/workspace.md +246 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +495 -0
  21. package/package.json +49 -0
  22. package/xanoscript_docs/README.md +1 -0
  23. package/xanoscript_docs/api_query_examples.md +1255 -0
  24. package/xanoscript_docs/api_query_guideline.md +129 -0
  25. package/xanoscript_docs/build_from_lovable.md +715 -0
  26. package/xanoscript_docs/db_query_guideline.md +427 -0
  27. package/xanoscript_docs/ephemeral_environment_guideline.md +529 -0
  28. package/xanoscript_docs/expression_guideline.md +1086 -0
  29. package/xanoscript_docs/frontend_guideline.md +67 -0
  30. package/xanoscript_docs/function_examples.md +1406 -0
  31. package/xanoscript_docs/function_guideline.md +130 -0
  32. package/xanoscript_docs/functions.md +2155 -0
  33. package/xanoscript_docs/input_guideline.md +227 -0
  34. package/xanoscript_docs/mcp_server_examples.md +36 -0
  35. package/xanoscript_docs/mcp_server_guideline.md +69 -0
  36. package/xanoscript_docs/query_filter.md +489 -0
  37. package/xanoscript_docs/table_examples.md +586 -0
  38. package/xanoscript_docs/table_guideline.md +137 -0
  39. package/xanoscript_docs/task_examples.md +511 -0
  40. package/xanoscript_docs/task_guideline.md +103 -0
  41. package/xanoscript_docs/tips_and_tricks.md +144 -0
  42. package/xanoscript_docs/tool_examples.md +69 -0
  43. package/xanoscript_docs/tool_guideline.md +139 -0
  44. package/xanoscript_docs/unit_testing_guideline.md +328 -0
  45. package/xanoscript_docs/version.json +3 -0
  46. package/xanoscript_docs/workspace.md +17 -0
package/dist/index.js ADDED
@@ -0,0 +1,495 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { readFileSync } from "fs";
6
+ import { fileURLToPath } from "url";
7
+ import { dirname, join } from "path";
8
+ import { xanoscriptParser } from "@xano/xanoscript-language-server/parser/parser.js";
9
+ import { getSchemeFromContent } from "@xano/xanoscript-language-server/utils.js";
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ // XanoScript docs mapping - keyword to files
13
+ // Run `npm run sync-docs` to regenerate this from the docs directory
14
+ const XANOSCRIPT_DOCS = {
15
+ // Core concepts (guideline + examples)
16
+ agent: ["agent_guideline.md", "agent_examples.md"],
17
+ api_query: ["api_query_guideline.md", "api_query_examples.md"],
18
+ function: ["function_guideline.md", "function_examples.md"],
19
+ mcp_server: ["mcp_server_guideline.md", "mcp_server_examples.md"],
20
+ table: ["table_guideline.md", "table_examples.md"],
21
+ task: ["task_guideline.md", "task_examples.md"],
22
+ tool: ["tool_guideline.md", "tool_examples.md"],
23
+ // Guideline only
24
+ db_query: ["db_query_guideline.md"],
25
+ ephemeral: ["ephemeral_environment_guideline.md"],
26
+ expressions: ["expression_guideline.md"],
27
+ frontend: ["frontend_guideline.md"],
28
+ input: ["input_guideline.md"],
29
+ testing: ["unit_testing_guideline.md"],
30
+ // Workflows (AI agent development guides)
31
+ workflow: ["AGENTS.md"],
32
+ api_workflow: ["API_AGENTS.md"],
33
+ function_workflow: ["FUNCTION_AGENTS.md"],
34
+ table_workflow: ["TABLE_AGENTS.md"],
35
+ task_workflow: ["TASK_AGENTS.md"],
36
+ // Standalone reference docs
37
+ lovable: ["build_from_lovable.md"],
38
+ syntax: ["functions.md"],
39
+ query_filter: ["query_filter.md"],
40
+ tips: ["tips_and_tricks.md"],
41
+ workspace: ["workspace.md"],
42
+ };
43
+ // Keyword aliases for convenience
44
+ const KEYWORD_ALIASES = {
45
+ // api_query
46
+ api: "api_query",
47
+ apis: "api_query",
48
+ endpoint: "api_query",
49
+ endpoints: "api_query",
50
+ query: "api_query",
51
+ // function
52
+ func: "function",
53
+ functions: "function",
54
+ // table
55
+ tables: "table",
56
+ schema: "table",
57
+ schemas: "table",
58
+ // task
59
+ tasks: "task",
60
+ cron: "task",
61
+ scheduled: "task",
62
+ // tool
63
+ tools: "tool",
64
+ // agent
65
+ agents: "agent",
66
+ ai_agent: "agent",
67
+ // mcp_server
68
+ mcp: "mcp_server",
69
+ // syntax
70
+ reference: "syntax",
71
+ ref: "syntax",
72
+ statements: "syntax",
73
+ stack: "syntax",
74
+ // expressions
75
+ expr: "expressions",
76
+ expression: "expressions",
77
+ filters: "expressions",
78
+ pipes: "expressions",
79
+ operators: "expressions",
80
+ // input
81
+ inputs: "input",
82
+ params: "input",
83
+ parameters: "input",
84
+ // db_query
85
+ db: "db_query",
86
+ database: "db_query",
87
+ // query_filter
88
+ filter: "query_filter",
89
+ where: "query_filter",
90
+ // workflow
91
+ workflows: "workflow",
92
+ dev: "workflow",
93
+ development: "workflow",
94
+ // testing
95
+ test: "testing",
96
+ tests: "testing",
97
+ unit_test: "testing",
98
+ // tips
99
+ tip: "tips",
100
+ tricks: "tips",
101
+ // frontend
102
+ ui: "frontend",
103
+ static: "frontend",
104
+ };
105
+ // Map of object names to their documentation files
106
+ const DOCS_MAP = {
107
+ workspace: "workspace.md",
108
+ table: "table.md",
109
+ api_group: "api_group.md",
110
+ function: "function.md",
111
+ task: "task.md",
112
+ middleware: "middleware.md",
113
+ addon: "addon.md",
114
+ agent: "agent.md",
115
+ tool: "tool.md",
116
+ mcp_server: "mcp_server.md",
117
+ realtime: "realtime.md",
118
+ triggers: "triggers.md",
119
+ file: "file.md",
120
+ history: "history.md",
121
+ authentication: "authentication.md",
122
+ };
123
+ // Get the api_docs directory path
124
+ function getDocsPath() {
125
+ // In development, look relative to src
126
+ // In production (after build), look relative to dist
127
+ const possiblePaths = [
128
+ join(__dirname, "..", "api_docs"),
129
+ join(__dirname, "..", "..", "api_docs"),
130
+ ];
131
+ for (const p of possiblePaths) {
132
+ try {
133
+ readFileSync(join(p, "index.md"));
134
+ return p;
135
+ }
136
+ catch {
137
+ continue;
138
+ }
139
+ }
140
+ return join(__dirname, "..", "api_docs");
141
+ }
142
+ const DOCS_PATH = getDocsPath();
143
+ // Get the xanoscript_docs directory path
144
+ function getXanoscriptDocsPath() {
145
+ const possiblePaths = [
146
+ join(__dirname, "..", "xanoscript_docs"),
147
+ join(__dirname, "..", "..", "xanoscript_docs"),
148
+ ];
149
+ for (const p of possiblePaths) {
150
+ try {
151
+ readFileSync(join(p, "version.json"));
152
+ return p;
153
+ }
154
+ catch {
155
+ continue;
156
+ }
157
+ }
158
+ return join(__dirname, "..", "xanoscript_docs");
159
+ }
160
+ const XANOSCRIPT_DOCS_PATH = getXanoscriptDocsPath();
161
+ function readDocumentation(object) {
162
+ try {
163
+ if (!object) {
164
+ // Return index documentation
165
+ return readFileSync(join(DOCS_PATH, "index.md"), "utf-8");
166
+ }
167
+ const normalizedObject = object.toLowerCase().trim();
168
+ // Check if the object exists in our map
169
+ if (normalizedObject in DOCS_MAP) {
170
+ const filePath = join(DOCS_PATH, DOCS_MAP[normalizedObject]);
171
+ return readFileSync(filePath, "utf-8");
172
+ }
173
+ // Try to find a partial match
174
+ const matchingKey = Object.keys(DOCS_MAP).find((key) => key.includes(normalizedObject) || normalizedObject.includes(key));
175
+ if (matchingKey) {
176
+ const filePath = join(DOCS_PATH, DOCS_MAP[matchingKey]);
177
+ return readFileSync(filePath, "utf-8");
178
+ }
179
+ // Return error message with available options
180
+ const availableObjects = Object.keys(DOCS_MAP).join(", ");
181
+ return `Error: Unknown object "${object}". Available objects: ${availableObjects}
182
+
183
+ Use api_docs() without parameters to see the full documentation index.`;
184
+ }
185
+ catch (error) {
186
+ const errorMessage = error instanceof Error ? error.message : String(error);
187
+ return `Error reading documentation: ${errorMessage}`;
188
+ }
189
+ }
190
+ // Read XanoScript documentation version
191
+ function getXanoscriptDocsVersion() {
192
+ try {
193
+ const versionFile = readFileSync(join(XANOSCRIPT_DOCS_PATH, "version.json"), "utf-8");
194
+ return JSON.parse(versionFile).version || "unknown";
195
+ }
196
+ catch {
197
+ return "unknown";
198
+ }
199
+ }
200
+ // Generate the XanoScript documentation index
201
+ function generateXanoscriptIndex() {
202
+ const version = getXanoscriptDocsVersion();
203
+ // Build alias lookup (keyword -> aliases)
204
+ const aliasLookup = {};
205
+ for (const [alias, keyword] of Object.entries(KEYWORD_ALIASES)) {
206
+ aliasLookup[keyword] = aliasLookup[keyword] || [];
207
+ aliasLookup[keyword].push(alias);
208
+ }
209
+ const formatRow = (keyword, description) => {
210
+ const aliases = aliasLookup[keyword]?.slice(0, 3).join(", ") || "";
211
+ return `| \`${keyword}\` | ${aliases ? aliases : "-"} | ${description} |`;
212
+ };
213
+ return `# XanoScript Documentation Index
214
+ Version: ${version}
215
+
216
+ Use \`xanoscript_docs\` with a keyword to retrieve documentation.
217
+
218
+ ## Core Concepts
219
+ These return guidelines + examples for writing XanoScript code.
220
+
221
+ | Keyword | Aliases | Description |
222
+ |---------|---------|-------------|
223
+ ${formatRow("function", "Custom reusable functions in `functions/`")}
224
+ ${formatRow("api_query", "HTTP API endpoints in `apis/`")}
225
+ ${formatRow("table", "Database table schemas in `tables/`")}
226
+ ${formatRow("task", "Scheduled background tasks in `tasks/`")}
227
+ ${formatRow("tool", "AI-callable tools in `tools/`")}
228
+ ${formatRow("agent", "AI agents in `agents/`")}
229
+ ${formatRow("mcp_server", "MCP servers in `mcp_servers/`")}
230
+
231
+ ## Language Reference
232
+ Core syntax and operators.
233
+
234
+ | Keyword | Aliases | Description |
235
+ |---------|---------|-------------|
236
+ ${formatRow("syntax", "Complete XanoScript syntax (stack, var, conditional, foreach, etc.)")}
237
+ ${formatRow("expressions", "Pipe operators and filters (string, math, array, date)")}
238
+ ${formatRow("input", "Input definition syntax (types, filters, validation)")}
239
+ ${formatRow("db_query", "Database query patterns (query, add, edit, delete)")}
240
+ ${formatRow("query_filter", "WHERE clause and filter syntax")}
241
+
242
+ ## Development Workflows
243
+ AI agent development strategies and phases.
244
+
245
+ | Keyword | Aliases | Description |
246
+ |---------|---------|-------------|
247
+ ${formatRow("workflow", "Overall XanoScript development workflow")}
248
+ ${formatRow("function_workflow", "AI workflow for creating functions")}
249
+ ${formatRow("api_workflow", "AI workflow for creating API endpoints")}
250
+ ${formatRow("table_workflow", "AI workflow for creating tables")}
251
+ ${formatRow("task_workflow", "AI workflow for creating tasks")}
252
+
253
+ ## Specialized Topics
254
+
255
+ | Keyword | Aliases | Description |
256
+ |---------|---------|-------------|
257
+ ${formatRow("frontend", "Frontend development with Xano")}
258
+ ${formatRow("lovable", "Building from Lovable-generated websites")}
259
+ ${formatRow("testing", "Unit testing XanoScript code")}
260
+ ${formatRow("tips", "Tips and tricks")}
261
+ ${formatRow("ephemeral", "Ephemeral environment setup")}
262
+ `;
263
+ }
264
+ // Read XanoScript documentation for a keyword
265
+ function readXanoscriptDocs(keyword) {
266
+ try {
267
+ if (!keyword) {
268
+ return generateXanoscriptIndex();
269
+ }
270
+ const normalizedKeyword = keyword.toLowerCase().trim();
271
+ // Check for alias first
272
+ const resolvedKeyword = KEYWORD_ALIASES[normalizedKeyword] || normalizedKeyword;
273
+ // Check if keyword exists
274
+ if (!(resolvedKeyword in XANOSCRIPT_DOCS)) {
275
+ // Try partial match
276
+ const matchingKey = Object.keys(XANOSCRIPT_DOCS).find((key) => key.includes(resolvedKeyword) || resolvedKeyword.includes(key));
277
+ if (matchingKey) {
278
+ return readXanoscriptDocs(matchingKey);
279
+ }
280
+ const availableKeywords = Object.keys(XANOSCRIPT_DOCS).join(", ");
281
+ return `Error: Unknown keyword "${keyword}". Available keywords: ${availableKeywords}
282
+
283
+ Use xanoscript_docs() without parameters to see the full documentation index.`;
284
+ }
285
+ const files = XANOSCRIPT_DOCS[resolvedKeyword];
286
+ const version = getXanoscriptDocsVersion();
287
+ // Read and concatenate all files for this keyword
288
+ const contents = [];
289
+ contents.push(`# XanoScript: ${resolvedKeyword}`);
290
+ contents.push(`Documentation version: ${version}\n`);
291
+ for (const file of files) {
292
+ const filePath = join(XANOSCRIPT_DOCS_PATH, file);
293
+ try {
294
+ const content = readFileSync(filePath, "utf-8");
295
+ contents.push(`---\n## Source: ${file}\n---\n`);
296
+ contents.push(content);
297
+ }
298
+ catch (err) {
299
+ contents.push(`\n[Error reading ${file}: file not found]\n`);
300
+ }
301
+ }
302
+ return contents.join("\n");
303
+ }
304
+ catch (error) {
305
+ const errorMessage = error instanceof Error ? error.message : String(error);
306
+ return `Error reading XanoScript documentation: ${errorMessage}`;
307
+ }
308
+ }
309
+ // Create the MCP server
310
+ const server = new Server({
311
+ name: "xano-developer-mcp",
312
+ version: "1.0.0",
313
+ }, {
314
+ capabilities: {
315
+ tools: {},
316
+ },
317
+ });
318
+ // List available tools
319
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
320
+ return {
321
+ tools: [
322
+ {
323
+ name: "api_docs",
324
+ description: "Get Xano Headless API documentation. Returns documentation for interacting with the Xano Headless API using XanoScript. Use without parameters for an overview, or specify an object for detailed documentation.",
325
+ inputSchema: {
326
+ type: "object",
327
+ properties: {
328
+ object: {
329
+ type: "string",
330
+ description: `Optional: The specific API object to get documentation for. Available values: ${Object.keys(DOCS_MAP).join(", ")}`,
331
+ },
332
+ },
333
+ required: [],
334
+ },
335
+ },
336
+ {
337
+ name: "validate_xanoscript",
338
+ description: "Validate XanoScript code for syntax errors. Returns a list of errors with line/column positions, or confirms the code is valid. The language server auto-detects the object type from the code syntax.",
339
+ inputSchema: {
340
+ type: "object",
341
+ properties: {
342
+ code: {
343
+ type: "string",
344
+ description: "The XanoScript code to validate",
345
+ },
346
+ },
347
+ required: ["code"],
348
+ },
349
+ },
350
+ {
351
+ name: "xanoscript_docs",
352
+ description: "Get XanoScript programming language documentation for AI code generation. " +
353
+ "Call without a keyword to see the full index of available topics. " +
354
+ "Use a keyword to retrieve specific documentation (guidelines + examples).",
355
+ inputSchema: {
356
+ type: "object",
357
+ properties: {
358
+ keyword: {
359
+ type: "string",
360
+ description: "Documentation topic to retrieve. " +
361
+ "Core: function, api_query (or 'api'), table, task, tool, agent, mcp_server. " +
362
+ "Reference: syntax (or 'ref'), expressions (or 'expr'), input, db_query (or 'db'). " +
363
+ "Workflow: workflow, function_workflow, api_workflow, table_workflow, task_workflow. " +
364
+ "Omit for the full documentation index.",
365
+ },
366
+ },
367
+ required: [],
368
+ },
369
+ },
370
+ ],
371
+ };
372
+ });
373
+ // Handle tool calls
374
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
375
+ if (request.params.name === "api_docs") {
376
+ const args = request.params.arguments;
377
+ const object = args?.object;
378
+ const documentation = readDocumentation(object);
379
+ return {
380
+ content: [
381
+ {
382
+ type: "text",
383
+ text: documentation,
384
+ },
385
+ ],
386
+ };
387
+ }
388
+ if (request.params.name === "validate_xanoscript") {
389
+ const args = request.params.arguments;
390
+ if (!args?.code) {
391
+ return {
392
+ content: [
393
+ {
394
+ type: "text",
395
+ text: "Error: 'code' parameter is required",
396
+ },
397
+ ],
398
+ isError: true,
399
+ };
400
+ }
401
+ try {
402
+ const text = args.code;
403
+ const scheme = getSchemeFromContent(text);
404
+ const parser = xanoscriptParser(text, scheme);
405
+ if (parser.errors.length === 0) {
406
+ return {
407
+ content: [
408
+ {
409
+ type: "text",
410
+ text: "✓ XanoScript is valid. No syntax errors found.",
411
+ },
412
+ ],
413
+ };
414
+ }
415
+ // Convert parser errors to diagnostics with line/column info
416
+ const diagnostics = parser.errors.map((error) => {
417
+ const startOffset = error.token?.startOffset ?? 0;
418
+ const endOffset = error.token?.endOffset ?? 5;
419
+ // Calculate line and character positions from offset
420
+ const lines = text.substring(0, startOffset).split("\n");
421
+ const line = lines.length - 1;
422
+ const character = lines[lines.length - 1].length;
423
+ const endLines = text.substring(0, endOffset + 1).split("\n");
424
+ const endLine = endLines.length - 1;
425
+ const endCharacter = endLines[endLines.length - 1].length;
426
+ return {
427
+ range: {
428
+ start: { line, character },
429
+ end: { line: endLine, character: endCharacter },
430
+ },
431
+ message: error.message,
432
+ source: error.name || "XanoScript Parser",
433
+ };
434
+ });
435
+ // Format errors for readable output
436
+ const errorMessages = diagnostics.map((d, i) => {
437
+ const location = `Line ${d.range.start.line + 1}, Column ${d.range.start.character + 1}`;
438
+ return `${i + 1}. [${location}] ${d.message}`;
439
+ });
440
+ return {
441
+ content: [
442
+ {
443
+ type: "text",
444
+ text: `Found ${diagnostics.length} error(s):\n\n${errorMessages.join("\n")}`,
445
+ },
446
+ ],
447
+ isError: true,
448
+ };
449
+ }
450
+ catch (error) {
451
+ const errorMessage = error instanceof Error ? error.message : String(error);
452
+ return {
453
+ content: [
454
+ {
455
+ type: "text",
456
+ text: `Validation error: ${errorMessage}`,
457
+ },
458
+ ],
459
+ isError: true,
460
+ };
461
+ }
462
+ }
463
+ if (request.params.name === "xanoscript_docs") {
464
+ const args = request.params.arguments;
465
+ const keyword = args?.keyword;
466
+ const documentation = readXanoscriptDocs(keyword);
467
+ return {
468
+ content: [
469
+ {
470
+ type: "text",
471
+ text: documentation,
472
+ },
473
+ ],
474
+ };
475
+ }
476
+ return {
477
+ content: [
478
+ {
479
+ type: "text",
480
+ text: `Unknown tool: ${request.params.name}`,
481
+ },
482
+ ],
483
+ isError: true,
484
+ };
485
+ });
486
+ // Start the server
487
+ async function main() {
488
+ const transport = new StdioServerTransport();
489
+ await server.connect(transport);
490
+ console.error("Xano Developer MCP server running on stdio");
491
+ }
492
+ main().catch((error) => {
493
+ console.error("Fatal error:", error);
494
+ process.exit(1);
495
+ });
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@xano/developer-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Xano Headless API documentation and XanoScript code validation",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "xano-developer-mcp": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "api_docs",
13
+ "xanoscript_docs"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "start": "node dist/index.js",
18
+ "dev": "tsc && node dist/index.js",
19
+ "prepublishOnly": "npm run build",
20
+ "sync-docs": "npx ts-node scripts/sync-xanoscript-docs.ts"
21
+ },
22
+ "keywords": [
23
+ "xano",
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "xanoscript",
27
+ "documentation",
28
+ "api",
29
+ "claude",
30
+ "ai"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/xano-inc/xano-developer-mcp.git"
35
+ },
36
+ "homepage": "https://github.com/xano-inc/xano-developer-mcp#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/xano-inc/xano-developer-mcp/issues"
39
+ },
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.26.0",
43
+ "@xano/xanoscript-language-server": "^11.6.3"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.0.0",
47
+ "typescript": "^5.9.0"
48
+ }
49
+ }
@@ -0,0 +1 @@
1
+ # xanoscript-ai-documentation