@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.
Files changed (42) hide show
  1. package/README.md +96 -31
  2. package/dist/index.js +248 -180
  3. package/package.json +4 -2
  4. package/xanoscript_docs/README.md +107 -1
  5. package/xanoscript_docs/agents.md +329 -0
  6. package/xanoscript_docs/apis.md +343 -0
  7. package/xanoscript_docs/database.md +417 -0
  8. package/xanoscript_docs/ephemeral.md +333 -0
  9. package/xanoscript_docs/frontend.md +291 -0
  10. package/xanoscript_docs/functions.md +232 -2035
  11. package/xanoscript_docs/integrations.md +439 -0
  12. package/xanoscript_docs/mcp-servers.md +190 -0
  13. package/xanoscript_docs/plan.md +192 -0
  14. package/xanoscript_docs/syntax.md +314 -0
  15. package/xanoscript_docs/tables.md +270 -0
  16. package/xanoscript_docs/tasks.md +254 -0
  17. package/xanoscript_docs/testing.md +335 -0
  18. package/xanoscript_docs/tools.md +305 -0
  19. package/xanoscript_docs/types.md +297 -0
  20. package/xanoscript_docs/version.json +2 -1
  21. package/xanoscript_docs/api_query_examples.md +0 -1255
  22. package/xanoscript_docs/api_query_guideline.md +0 -129
  23. package/xanoscript_docs/build_from_lovable.md +0 -715
  24. package/xanoscript_docs/db_query_guideline.md +0 -427
  25. package/xanoscript_docs/ephemeral_environment_guideline.md +0 -529
  26. package/xanoscript_docs/expression_guideline.md +0 -1086
  27. package/xanoscript_docs/frontend_guideline.md +0 -67
  28. package/xanoscript_docs/function_examples.md +0 -1406
  29. package/xanoscript_docs/function_guideline.md +0 -130
  30. package/xanoscript_docs/input_guideline.md +0 -227
  31. package/xanoscript_docs/mcp_server_examples.md +0 -36
  32. package/xanoscript_docs/mcp_server_guideline.md +0 -69
  33. package/xanoscript_docs/query_filter.md +0 -489
  34. package/xanoscript_docs/table_examples.md +0 -586
  35. package/xanoscript_docs/table_guideline.md +0 -137
  36. package/xanoscript_docs/task_examples.md +0 -511
  37. package/xanoscript_docs/task_guideline.md +0 -103
  38. package/xanoscript_docs/tips_and_tricks.md +0 -144
  39. package/xanoscript_docs/tool_examples.md +0 -69
  40. package/xanoscript_docs/tool_guideline.md +0 -139
  41. package/xanoscript_docs/unit_testing_guideline.md +0 -328
  42. 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
- // XanoScript docs mapping - keyword to files
15
- // Run `npm run sync-docs` to regenerate this from the docs directory
16
- const XANOSCRIPT_DOCS = {
17
- // Core concepts (guideline + examples)
18
- agent: ["agent_guideline.md", "agent_examples.md"],
19
- api_query: ["api_query_guideline.md", "api_query_examples.md"],
20
- function: ["function_guideline.md", "function_examples.md"],
21
- mcp_server: ["mcp_server_guideline.md", "mcp_server_examples.md"],
22
- table: ["table_guideline.md", "table_examples.md"],
23
- task: ["task_guideline.md", "task_examples.md"],
24
- tool: ["tool_guideline.md", "tool_examples.md"],
25
- // Guideline only
26
- db_query: ["db_query_guideline.md"],
27
- ephemeral: ["ephemeral_environment_guideline.md"],
28
- expressions: ["expression_guideline.md"],
29
- frontend: ["frontend_guideline.md"],
30
- input: ["input_guideline.md"],
31
- testing: ["unit_testing_guideline.md"],
32
- // Workflows (AI agent development guides)
33
- workflow: ["AGENTS.md"],
34
- api_workflow: ["API_AGENTS.md"],
35
- function_workflow: ["FUNCTION_AGENTS.md"],
36
- table_workflow: ["TABLE_AGENTS.md"],
37
- task_workflow: ["TASK_AGENTS.md"],
38
- // Standalone reference docs
39
- lovable: ["build_from_lovable.md"],
40
- syntax: ["functions.md"],
41
- query_filter: ["query_filter.md"],
42
- tips: ["tips_and_tricks.md"],
43
- workspace: ["workspace.md"],
44
- };
45
- // Keyword aliases for convenience
46
- const KEYWORD_ALIASES = {
47
- // api_query
48
- api: "api_query",
49
- apis: "api_query",
50
- endpoint: "api_query",
51
- endpoints: "api_query",
52
- query: "api_query",
53
- // function
54
- func: "function",
55
- functions: "function",
56
- // table
57
- tables: "table",
58
- schema: "table",
59
- schemas: "table",
60
- // task
61
- tasks: "task",
62
- cron: "task",
63
- scheduled: "task",
64
- // tool
65
- tools: "tool",
66
- // agent
67
- agents: "agent",
68
- ai_agent: "agent",
69
- // mcp_server
70
- mcp: "mcp_server",
71
- // syntax
72
- reference: "syntax",
73
- ref: "syntax",
74
- statements: "syntax",
75
- stack: "syntax",
76
- // expressions
77
- expr: "expressions",
78
- expression: "expressions",
79
- filters: "expressions",
80
- pipes: "expressions",
81
- operators: "expressions",
82
- // input
83
- inputs: "input",
84
- params: "input",
85
- parameters: "input",
86
- // db_query
87
- db: "db_query",
88
- database: "db_query",
89
- // query_filter
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
- // Generate init_workspace documentation
171
- function generateInitWorkspaceDoc() {
172
- const objectTypes = Object.entries(XANO_OBJECT_TYPES).map(([type, config]) => ({
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
- // Get the api_docs directory path
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
- // Read XanoScript documentation version
265
- function getXanoscriptDocsVersion() {
266
- try {
267
- const versionFile = readFileSync(join(XANOSCRIPT_DOCS_PATH, "version.json"), "utf-8");
268
- return JSON.parse(versionFile).version || "unknown";
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
- catch {
271
- return "unknown";
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
- // Generate the XanoScript documentation index
275
- function generateXanoscriptIndex() {
276
- const version = getXanoscriptDocsVersion();
277
- // Build alias lookup (keyword -> aliases)
278
- const aliasLookup = {};
279
- for (const [alias, keyword] of Object.entries(KEYWORD_ALIASES)) {
280
- aliasLookup[keyword] = aliasLookup[keyword] || [];
281
- aliasLookup[keyword].push(alias);
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
- return generateXanoscriptIndexTemplate({ version, aliasLookup });
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
- // Read XanoScript documentation for a keyword
286
- function readXanoscriptDocs(keyword) {
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
- if (!keyword) {
289
- return generateXanoscriptIndex();
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
- const normalizedKeyword = keyword.toLowerCase().trim();
292
- // Check for alias first
293
- const resolvedKeyword = KEYWORD_ALIASES[normalizedKeyword] || normalizedKeyword;
294
- // Check if keyword exists
295
- if (!(resolvedKeyword in XANOSCRIPT_DOCS)) {
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 availableKeywords = Object.keys(XANOSCRIPT_DOCS).join(", ");
302
- return `Error: Unknown keyword "${keyword}". Available keywords: ${availableKeywords}
303
-
304
- Use xanoscript_docs() without parameters to see the full documentation index.`;
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
- const files = XANOSCRIPT_DOCS[resolvedKeyword];
307
- const version = getXanoscriptDocsVersion();
308
- // Read and concatenate all files for this keyword
309
- const contents = [];
310
- contents.push(`# XanoScript: ${resolvedKeyword}`);
311
- contents.push(`Documentation version: ${version}\n`);
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 contents.join("\n");
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
- // Create the MCP server
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
- // List available tools
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 a keyword to see the full index of available topics. " +
376
- "Use a keyword to retrieve specific documentation (guidelines + examples).",
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
- keyword: {
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
- description: "Documentation topic to retrieve. " +
383
- "Core: function, api_query (or 'api'), table, task, tool, agent, mcp_server. " +
384
- "Reference: syntax (or 'ref'), expressions (or 'expr'), input, db_query (or 'db'). " +
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: "XanoScript is valid. No syntax errors found.",
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 keyword = args?.keyword;
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
- // Start the server
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.1",
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
  }