@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
package/dist/index.js
CHANGED
|
@@ -1,108 +1,92 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { readFileSync } from "fs";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { dirname, join } from "path";
|
|
8
|
+
import { minimatch } from "minimatch";
|
|
8
9
|
import { xanoscriptParser } from "@xano/xanoscript-language-server/parser/parser.js";
|
|
9
10
|
import { getSchemeFromContent } from "@xano/xanoscript-language-server/utils.js";
|
|
10
11
|
import { generateInitWorkspaceTemplate } from "./templates/init-workspace.js";
|
|
11
|
-
import { generateXanoscriptIndexTemplate } from "./templates/xanoscript-index.js";
|
|
12
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
13
|
const __dirname = dirname(__filename);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
filter: "query_filter",
|
|
91
|
-
where: "query_filter",
|
|
92
|
-
// workflow
|
|
93
|
-
workflows: "workflow",
|
|
94
|
-
dev: "workflow",
|
|
95
|
-
development: "workflow",
|
|
96
|
-
// testing
|
|
97
|
-
test: "testing",
|
|
98
|
-
tests: "testing",
|
|
99
|
-
unit_test: "testing",
|
|
100
|
-
// tips
|
|
101
|
-
tip: "tips",
|
|
102
|
-
tricks: "tips",
|
|
103
|
-
// frontend
|
|
104
|
-
ui: "frontend",
|
|
105
|
-
static: "frontend",
|
|
14
|
+
const XANOSCRIPT_DOCS_V2 = {
|
|
15
|
+
readme: {
|
|
16
|
+
file: "README.md",
|
|
17
|
+
applyTo: [],
|
|
18
|
+
description: "XanoScript overview, workspace structure, and quick reference",
|
|
19
|
+
},
|
|
20
|
+
syntax: {
|
|
21
|
+
file: "syntax.md",
|
|
22
|
+
applyTo: ["**/*.xs"],
|
|
23
|
+
description: "Expressions, operators, and filters for all XanoScript code",
|
|
24
|
+
},
|
|
25
|
+
types: {
|
|
26
|
+
file: "types.md",
|
|
27
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tools/**/*.xs", "agents/**/*.xs"],
|
|
28
|
+
description: "Data types, input blocks, and validation",
|
|
29
|
+
},
|
|
30
|
+
tables: {
|
|
31
|
+
file: "tables.md",
|
|
32
|
+
applyTo: ["tables/*.xs"],
|
|
33
|
+
description: "Database schema definitions with indexes and relationships",
|
|
34
|
+
},
|
|
35
|
+
functions: {
|
|
36
|
+
file: "functions.md",
|
|
37
|
+
applyTo: ["functions/**/*.xs"],
|
|
38
|
+
description: "Reusable function stacks with inputs and responses",
|
|
39
|
+
},
|
|
40
|
+
apis: {
|
|
41
|
+
file: "apis.md",
|
|
42
|
+
applyTo: ["apis/**/*.xs"],
|
|
43
|
+
description: "HTTP endpoint definitions with authentication and CRUD patterns",
|
|
44
|
+
},
|
|
45
|
+
tasks: {
|
|
46
|
+
file: "tasks.md",
|
|
47
|
+
applyTo: ["tasks/*.xs"],
|
|
48
|
+
description: "Scheduled and cron jobs",
|
|
49
|
+
},
|
|
50
|
+
database: {
|
|
51
|
+
file: "database.md",
|
|
52
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs", "tools/**/*.xs"],
|
|
53
|
+
description: "All db.* operations: query, get, add, edit, patch, delete",
|
|
54
|
+
},
|
|
55
|
+
agents: {
|
|
56
|
+
file: "agents.md",
|
|
57
|
+
applyTo: ["agents/**/*.xs"],
|
|
58
|
+
description: "AI agent configuration with LLM providers and tools",
|
|
59
|
+
},
|
|
60
|
+
tools: {
|
|
61
|
+
file: "tools.md",
|
|
62
|
+
applyTo: ["tools/**/*.xs"],
|
|
63
|
+
description: "AI tools for agents and MCP servers",
|
|
64
|
+
},
|
|
65
|
+
"mcp-servers": {
|
|
66
|
+
file: "mcp-servers.md",
|
|
67
|
+
applyTo: ["mcp_servers/**/*.xs"],
|
|
68
|
+
description: "MCP server definitions exposing tools",
|
|
69
|
+
},
|
|
70
|
+
testing: {
|
|
71
|
+
file: "testing.md",
|
|
72
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
73
|
+
description: "Unit tests, mocks, and assertions",
|
|
74
|
+
},
|
|
75
|
+
integrations: {
|
|
76
|
+
file: "integrations.md",
|
|
77
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs"],
|
|
78
|
+
description: "Cloud storage, Redis, security, and external APIs",
|
|
79
|
+
},
|
|
80
|
+
frontend: {
|
|
81
|
+
file: "frontend.md",
|
|
82
|
+
applyTo: ["static/**/*"],
|
|
83
|
+
description: "Static frontend development and deployment",
|
|
84
|
+
},
|
|
85
|
+
ephemeral: {
|
|
86
|
+
file: "ephemeral.md",
|
|
87
|
+
applyTo: ["ephemeral/**/*.xs"],
|
|
88
|
+
description: "Temporary test environments",
|
|
89
|
+
},
|
|
106
90
|
};
|
|
107
91
|
const XANO_OBJECT_TYPES = {
|
|
108
92
|
function: {
|
|
@@ -167,16 +151,9 @@ const XANO_OBJECT_TYPES = {
|
|
|
167
151
|
hasXanoscript: true,
|
|
168
152
|
},
|
|
169
153
|
};
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
type,
|
|
174
|
-
path: config.path,
|
|
175
|
-
endpoint: config.endpoint,
|
|
176
|
-
}));
|
|
177
|
-
return generateInitWorkspaceTemplate(objectTypes);
|
|
178
|
-
}
|
|
179
|
-
// Map of object names to their documentation files
|
|
154
|
+
// =============================================================================
|
|
155
|
+
// API Documentation Configuration
|
|
156
|
+
// =============================================================================
|
|
180
157
|
const DOCS_MAP = {
|
|
181
158
|
workspace: "workspace.md",
|
|
182
159
|
table: "table.md",
|
|
@@ -194,10 +171,10 @@ const DOCS_MAP = {
|
|
|
194
171
|
history: "history.md",
|
|
195
172
|
authentication: "authentication.md",
|
|
196
173
|
};
|
|
197
|
-
//
|
|
174
|
+
// =============================================================================
|
|
175
|
+
// Path Resolution
|
|
176
|
+
// =============================================================================
|
|
198
177
|
function getDocsPath() {
|
|
199
|
-
// In development, look relative to src
|
|
200
|
-
// In production (after build), look relative to dist
|
|
201
178
|
const possiblePaths = [
|
|
202
179
|
join(__dirname, "..", "api_docs"),
|
|
203
180
|
join(__dirname, "..", "..", "api_docs"),
|
|
@@ -213,8 +190,6 @@ function getDocsPath() {
|
|
|
213
190
|
}
|
|
214
191
|
return join(__dirname, "..", "api_docs");
|
|
215
192
|
}
|
|
216
|
-
const DOCS_PATH = getDocsPath();
|
|
217
|
-
// Get the xanoscript_docs directory path
|
|
218
193
|
function getXanoscriptDocsPath() {
|
|
219
194
|
const possiblePaths = [
|
|
220
195
|
join(__dirname, "..", "xanoscript_docs"),
|
|
@@ -231,26 +206,35 @@ function getXanoscriptDocsPath() {
|
|
|
231
206
|
}
|
|
232
207
|
return join(__dirname, "..", "xanoscript_docs");
|
|
233
208
|
}
|
|
209
|
+
const DOCS_PATH = getDocsPath();
|
|
234
210
|
const XANOSCRIPT_DOCS_PATH = getXanoscriptDocsPath();
|
|
211
|
+
// =============================================================================
|
|
212
|
+
// Documentation Helpers
|
|
213
|
+
// =============================================================================
|
|
214
|
+
function getXanoscriptDocsVersion() {
|
|
215
|
+
try {
|
|
216
|
+
const versionFile = readFileSync(join(XANOSCRIPT_DOCS_PATH, "version.json"), "utf-8");
|
|
217
|
+
return JSON.parse(versionFile).version || "unknown";
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return "unknown";
|
|
221
|
+
}
|
|
222
|
+
}
|
|
235
223
|
function readDocumentation(object) {
|
|
236
224
|
try {
|
|
237
225
|
if (!object) {
|
|
238
|
-
// Return index documentation
|
|
239
226
|
return readFileSync(join(DOCS_PATH, "index.md"), "utf-8");
|
|
240
227
|
}
|
|
241
228
|
const normalizedObject = object.toLowerCase().trim();
|
|
242
|
-
// Check if the object exists in our map
|
|
243
229
|
if (normalizedObject in DOCS_MAP) {
|
|
244
230
|
const filePath = join(DOCS_PATH, DOCS_MAP[normalizedObject]);
|
|
245
231
|
return readFileSync(filePath, "utf-8");
|
|
246
232
|
}
|
|
247
|
-
// Try to find a partial match
|
|
248
233
|
const matchingKey = Object.keys(DOCS_MAP).find((key) => key.includes(normalizedObject) || normalizedObject.includes(key));
|
|
249
234
|
if (matchingKey) {
|
|
250
235
|
const filePath = join(DOCS_PATH, DOCS_MAP[matchingKey]);
|
|
251
236
|
return readFileSync(filePath, "utf-8");
|
|
252
237
|
}
|
|
253
|
-
// Return error message with available options
|
|
254
238
|
const availableObjects = Object.keys(DOCS_MAP).join(", ");
|
|
255
239
|
return `Error: Unknown object "${object}". Available objects: ${availableObjects}
|
|
256
240
|
|
|
@@ -261,73 +245,111 @@ Use api_docs() without parameters to see the full documentation index.`;
|
|
|
261
245
|
return `Error reading documentation: ${errorMessage}`;
|
|
262
246
|
}
|
|
263
247
|
}
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
248
|
+
// =============================================================================
|
|
249
|
+
// XanoScript Documentation v2 Functions
|
|
250
|
+
// =============================================================================
|
|
251
|
+
/**
|
|
252
|
+
* Get list of topics that apply to a given file path based on applyTo patterns
|
|
253
|
+
*/
|
|
254
|
+
function getDocsForFilePath(filePath) {
|
|
255
|
+
const matches = [];
|
|
256
|
+
for (const [topic, config] of Object.entries(XANOSCRIPT_DOCS_V2)) {
|
|
257
|
+
if (topic === "readme")
|
|
258
|
+
continue; // Don't auto-include readme
|
|
259
|
+
for (const pattern of config.applyTo) {
|
|
260
|
+
if (minimatch(filePath, pattern)) {
|
|
261
|
+
matches.push(topic);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
269
265
|
}
|
|
270
|
-
|
|
271
|
-
|
|
266
|
+
// Always include syntax as foundation (if not already matched)
|
|
267
|
+
if (!matches.includes("syntax")) {
|
|
268
|
+
matches.unshift("syntax");
|
|
272
269
|
}
|
|
270
|
+
return matches;
|
|
273
271
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Extract just the Quick Reference section from a doc
|
|
274
|
+
*/
|
|
275
|
+
function extractQuickReference(content, topic) {
|
|
276
|
+
const lines = content.split("\n");
|
|
277
|
+
const startIdx = lines.findIndex((l) => l.startsWith("## Quick Reference"));
|
|
278
|
+
if (startIdx === -1) {
|
|
279
|
+
// Fallback: return first 50 lines or up to first ## section
|
|
280
|
+
const firstSection = lines.findIndex((l, i) => i > 0 && l.startsWith("## "));
|
|
281
|
+
return lines.slice(0, firstSection > 0 ? firstSection : 50).join("\n");
|
|
282
282
|
}
|
|
283
|
-
|
|
283
|
+
// Find the next ## section after Quick Reference
|
|
284
|
+
let endIdx = lines.findIndex((l, i) => i > startIdx && l.startsWith("## "));
|
|
285
|
+
if (endIdx === -1)
|
|
286
|
+
endIdx = lines.length;
|
|
287
|
+
// Include topic header for context
|
|
288
|
+
const header = `# ${topic}\n\n`;
|
|
289
|
+
return header + lines.slice(startIdx, endIdx).join("\n");
|
|
284
290
|
}
|
|
285
|
-
|
|
286
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Read XanoScript documentation with new v2 structure
|
|
293
|
+
*/
|
|
294
|
+
function readXanoscriptDocsV2(args) {
|
|
295
|
+
const mode = args?.mode || "full";
|
|
296
|
+
const version = getXanoscriptDocsVersion();
|
|
287
297
|
try {
|
|
288
|
-
|
|
289
|
-
|
|
298
|
+
// Default: return README
|
|
299
|
+
if (!args?.topic && !args?.file_path) {
|
|
300
|
+
const readme = readFileSync(join(XANOSCRIPT_DOCS_PATH, "README.md"), "utf-8");
|
|
301
|
+
return `${readme}\n\n---\nDocumentation version: ${version}`;
|
|
290
302
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// Try partial match
|
|
297
|
-
const matchingKey = Object.keys(XANOSCRIPT_DOCS).find((key) => key.includes(resolvedKeyword) || resolvedKeyword.includes(key));
|
|
298
|
-
if (matchingKey) {
|
|
299
|
-
return readXanoscriptDocs(matchingKey);
|
|
303
|
+
// Context-aware: return docs matching file pattern
|
|
304
|
+
if (args?.file_path) {
|
|
305
|
+
const topics = getDocsForFilePath(args.file_path);
|
|
306
|
+
if (topics.length === 0) {
|
|
307
|
+
return `No documentation found for file pattern: ${args.file_path}\n\nAvailable topics: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`;
|
|
300
308
|
}
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
const docs = topics.map((t) => {
|
|
310
|
+
const config = XANOSCRIPT_DOCS_V2[t];
|
|
311
|
+
const content = readFileSync(join(XANOSCRIPT_DOCS_PATH, config.file), "utf-8");
|
|
312
|
+
return mode === "quick_reference"
|
|
313
|
+
? extractQuickReference(content, t)
|
|
314
|
+
: content;
|
|
315
|
+
});
|
|
316
|
+
const header = `# XanoScript Documentation for: ${args.file_path}\n\nMatched topics: ${topics.join(", ")}\nMode: ${mode}\nVersion: ${version}\n\n---\n\n`;
|
|
317
|
+
return header + docs.join("\n\n---\n\n");
|
|
305
318
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
for (const file of files) {
|
|
313
|
-
const filePath = join(XANOSCRIPT_DOCS_PATH, file);
|
|
314
|
-
try {
|
|
315
|
-
const content = readFileSync(filePath, "utf-8");
|
|
316
|
-
contents.push(`---\n## Source: ${file}\n---\n`);
|
|
317
|
-
contents.push(content);
|
|
318
|
-
}
|
|
319
|
-
catch (err) {
|
|
320
|
-
contents.push(`\n[Error reading ${file}: file not found]\n`);
|
|
319
|
+
// Topic-based: return specific doc
|
|
320
|
+
if (args?.topic) {
|
|
321
|
+
const config = XANOSCRIPT_DOCS_V2[args.topic];
|
|
322
|
+
if (!config) {
|
|
323
|
+
const availableTopics = Object.keys(XANOSCRIPT_DOCS_V2).join(", ");
|
|
324
|
+
return `Error: Unknown topic "${args.topic}".\n\nAvailable topics: ${availableTopics}`;
|
|
321
325
|
}
|
|
326
|
+
const content = readFileSync(join(XANOSCRIPT_DOCS_PATH, config.file), "utf-8");
|
|
327
|
+
const doc = mode === "quick_reference"
|
|
328
|
+
? extractQuickReference(content, args.topic)
|
|
329
|
+
: content;
|
|
330
|
+
return `${doc}\n\n---\nDocumentation version: ${version}`;
|
|
322
331
|
}
|
|
323
|
-
return
|
|
332
|
+
return "Error: Invalid parameters";
|
|
324
333
|
}
|
|
325
334
|
catch (error) {
|
|
326
335
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
327
336
|
return `Error reading XanoScript documentation: ${errorMessage}`;
|
|
328
337
|
}
|
|
329
338
|
}
|
|
330
|
-
//
|
|
339
|
+
// =============================================================================
|
|
340
|
+
// Init Workspace Documentation
|
|
341
|
+
// =============================================================================
|
|
342
|
+
function generateInitWorkspaceDoc() {
|
|
343
|
+
const objectTypes = Object.entries(XANO_OBJECT_TYPES).map(([type, config]) => ({
|
|
344
|
+
type,
|
|
345
|
+
path: config.path,
|
|
346
|
+
endpoint: config.endpoint,
|
|
347
|
+
}));
|
|
348
|
+
return generateInitWorkspaceTemplate(objectTypes);
|
|
349
|
+
}
|
|
350
|
+
// =============================================================================
|
|
351
|
+
// MCP Server Setup
|
|
352
|
+
// =============================================================================
|
|
331
353
|
const server = new Server({
|
|
332
354
|
name: "xano-developer-mcp",
|
|
333
355
|
version: "1.0.0",
|
|
@@ -335,9 +357,47 @@ const server = new Server({
|
|
|
335
357
|
}, {
|
|
336
358
|
capabilities: {
|
|
337
359
|
tools: {},
|
|
360
|
+
resources: {},
|
|
338
361
|
},
|
|
339
362
|
});
|
|
340
|
-
//
|
|
363
|
+
// =============================================================================
|
|
364
|
+
// Resource Handlers (MCP Resources)
|
|
365
|
+
// =============================================================================
|
|
366
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
367
|
+
const resources = Object.entries(XANOSCRIPT_DOCS_V2).map(([key, config]) => ({
|
|
368
|
+
uri: `xanoscript://docs/${key}`,
|
|
369
|
+
name: key,
|
|
370
|
+
description: config.description,
|
|
371
|
+
mimeType: "text/markdown",
|
|
372
|
+
}));
|
|
373
|
+
return { resources };
|
|
374
|
+
});
|
|
375
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
376
|
+
const { uri } = request.params;
|
|
377
|
+
const match = uri.match(/^xanoscript:\/\/docs\/(.+)$/);
|
|
378
|
+
if (!match) {
|
|
379
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
380
|
+
}
|
|
381
|
+
const topic = match[1];
|
|
382
|
+
const config = XANOSCRIPT_DOCS_V2[topic];
|
|
383
|
+
if (!config) {
|
|
384
|
+
throw new Error(`Unknown topic: ${topic}. Available: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`);
|
|
385
|
+
}
|
|
386
|
+
const content = readFileSync(join(XANOSCRIPT_DOCS_PATH, config.file), "utf-8");
|
|
387
|
+
const version = getXanoscriptDocsVersion();
|
|
388
|
+
return {
|
|
389
|
+
contents: [
|
|
390
|
+
{
|
|
391
|
+
uri,
|
|
392
|
+
mimeType: "text/markdown",
|
|
393
|
+
text: `${content}\n\n---\nDocumentation version: ${version}`,
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
};
|
|
397
|
+
});
|
|
398
|
+
// =============================================================================
|
|
399
|
+
// Tool Handlers
|
|
400
|
+
// =============================================================================
|
|
341
401
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
342
402
|
return {
|
|
343
403
|
tools: [
|
|
@@ -372,18 +432,29 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
372
432
|
{
|
|
373
433
|
name: "xanoscript_docs",
|
|
374
434
|
description: "Get XanoScript programming language documentation for AI code generation. " +
|
|
375
|
-
"Call without
|
|
376
|
-
"Use
|
|
435
|
+
"Call without parameters for overview (README). " +
|
|
436
|
+
"Use 'topic' for specific documentation, or 'file_path' for context-aware docs based on the file you're editing. " +
|
|
437
|
+
"Use mode='quick_reference' for compact syntax cheatsheet (recommended for context efficiency).",
|
|
377
438
|
inputSchema: {
|
|
378
439
|
type: "object",
|
|
379
440
|
properties: {
|
|
380
|
-
|
|
441
|
+
topic: {
|
|
442
|
+
type: "string",
|
|
443
|
+
description: "Documentation topic. Available: " +
|
|
444
|
+
Object.entries(XANOSCRIPT_DOCS_V2)
|
|
445
|
+
.map(([k, v]) => `${k} (${v.description.split(".")[0]})`)
|
|
446
|
+
.join(", "),
|
|
447
|
+
},
|
|
448
|
+
file_path: {
|
|
449
|
+
type: "string",
|
|
450
|
+
description: "File path being edited (e.g., 'apis/users/create.xs', 'functions/utils/format.xs'). " +
|
|
451
|
+
"Returns all relevant docs based on file type using applyTo pattern matching.",
|
|
452
|
+
},
|
|
453
|
+
mode: {
|
|
381
454
|
type: "string",
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
"
|
|
385
|
-
"Workflow: workflow, function_workflow, api_workflow, table_workflow, task_workflow. " +
|
|
386
|
-
"Omit for the full documentation index.",
|
|
455
|
+
enum: ["full", "quick_reference"],
|
|
456
|
+
description: "full = complete documentation, quick_reference = compact Quick Reference sections only. " +
|
|
457
|
+
"Use quick_reference for smaller context window usage.",
|
|
387
458
|
},
|
|
388
459
|
},
|
|
389
460
|
required: [],
|
|
@@ -404,7 +475,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
404
475
|
],
|
|
405
476
|
};
|
|
406
477
|
});
|
|
407
|
-
// Handle tool calls
|
|
408
478
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
409
479
|
if (request.params.name === "api_docs") {
|
|
410
480
|
const args = request.params.arguments;
|
|
@@ -441,16 +511,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
441
511
|
content: [
|
|
442
512
|
{
|
|
443
513
|
type: "text",
|
|
444
|
-
text: "
|
|
514
|
+
text: "XanoScript is valid. No syntax errors found.",
|
|
445
515
|
},
|
|
446
516
|
],
|
|
447
517
|
};
|
|
448
518
|
}
|
|
449
|
-
// Convert parser errors to diagnostics with line/column info
|
|
450
519
|
const diagnostics = parser.errors.map((error) => {
|
|
451
520
|
const startOffset = error.token?.startOffset ?? 0;
|
|
452
521
|
const endOffset = error.token?.endOffset ?? 5;
|
|
453
|
-
// Calculate line and character positions from offset
|
|
454
522
|
const lines = text.substring(0, startOffset).split("\n");
|
|
455
523
|
const line = lines.length - 1;
|
|
456
524
|
const character = lines[lines.length - 1].length;
|
|
@@ -466,7 +534,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
466
534
|
source: error.name || "XanoScript Parser",
|
|
467
535
|
};
|
|
468
536
|
});
|
|
469
|
-
// Format errors for readable output
|
|
470
537
|
const errorMessages = diagnostics.map((d, i) => {
|
|
471
538
|
const location = `Line ${d.range.start.line + 1}, Column ${d.range.start.character + 1}`;
|
|
472
539
|
return `${i + 1}. [${location}] ${d.message}`;
|
|
@@ -496,8 +563,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
496
563
|
}
|
|
497
564
|
if (request.params.name === "xanoscript_docs") {
|
|
498
565
|
const args = request.params.arguments;
|
|
499
|
-
const
|
|
500
|
-
const documentation = readXanoscriptDocs(keyword);
|
|
566
|
+
const documentation = readXanoscriptDocsV2(args);
|
|
501
567
|
return {
|
|
502
568
|
content: [
|
|
503
569
|
{
|
|
@@ -528,7 +594,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
528
594
|
isError: true,
|
|
529
595
|
};
|
|
530
596
|
});
|
|
531
|
-
//
|
|
597
|
+
// =============================================================================
|
|
598
|
+
// Start Server
|
|
599
|
+
// =============================================================================
|
|
532
600
|
async function main() {
|
|
533
601
|
const transport = new StdioServerTransport();
|
|
534
602
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xano/developer-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "MCP server for Xano Headless API documentation and XanoScript code validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -40,9 +40,11 @@
|
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
43
|
-
"@xano/xanoscript-language-server": "^11.6.3"
|
|
43
|
+
"@xano/xanoscript-language-server": "^11.6.3",
|
|
44
|
+
"minimatch": "^10.1.2"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
47
|
+
"@types/minimatch": "^5.1.2",
|
|
46
48
|
"@types/node": "^22.0.0",
|
|
47
49
|
"typescript": "^5.9.0"
|
|
48
50
|
}
|