@xano/developer-mcp 1.0.34 → 1.0.35

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.
@@ -29,33 +29,20 @@
29
29
  * const docs = xanoscriptDocs({ topic: 'syntax' });
30
30
  * ```
31
31
  */
32
- import { validateXanoscript, validateXanoscriptTool, validateXanoscriptToolDefinition, type ValidateXanoscriptArgs, type ValidationResult, type ParserDiagnostic } from "./validate_xanoscript.js";
32
+ import { validateXanoscript, validateXanoscriptTool, validateXanoscriptToolDefinition, TYPE_ALIASES, RESERVED_VARIABLES, type ValidateXanoscriptArgs, type ValidationResult, type BatchValidationResult, type SingleFileValidationResult, type ParserDiagnostic } from "./validate_xanoscript.js";
33
33
  import { xanoscriptDocs, xanoscriptDocsTool, xanoscriptDocsToolDefinition, getXanoscriptDocsPath, setXanoscriptDocsPath, type XanoscriptDocsArgs, type XanoscriptDocsResult } from "./xanoscript_docs.js";
34
34
  import { mcpVersion, mcpVersionTool, mcpVersionToolDefinition, getServerVersion, setServerVersion, type McpVersionResult } from "./mcp_version.js";
35
35
  import { metaApiDocs, metaApiDocsTool, metaApiDocsToolDefinition, metaApiTopics, getMetaApiTopicNames, getMetaApiTopicDescriptions, type MetaApiDocsArgs, type MetaApiDocsResult } from "./meta_api_docs.js";
36
36
  import { runApiDocs, runApiDocsTool, runApiDocsToolDefinition, runApiTopics, getRunApiTopicNames, getRunApiTopicDescriptions, type RunApiDocsArgs, type RunApiDocsResult } from "./run_api_docs.js";
37
37
  import { cliDocs, cliDocsTool, cliDocsToolDefinition, cliTopics, getCliTopicNames, getCliTopicDescriptions, type CliDocsArgs, type CliDocsResult } from "./cli_docs.js";
38
38
  import { type ToolResult, toMcpResponse } from "./types.js";
39
- export { validateXanoscript, type ValidateXanoscriptArgs, type ValidationResult, type ParserDiagnostic, xanoscriptDocs, getXanoscriptDocsPath, setXanoscriptDocsPath, type XanoscriptDocsArgs, type XanoscriptDocsResult, mcpVersion, getServerVersion, setServerVersion, type McpVersionResult, metaApiDocs, metaApiTopics, getMetaApiTopicNames, getMetaApiTopicDescriptions, type MetaApiDocsArgs, type MetaApiDocsResult, runApiDocs, runApiTopics, getRunApiTopicNames, getRunApiTopicDescriptions, type RunApiDocsArgs, type RunApiDocsResult, cliDocs, cliTopics, getCliTopicNames, getCliTopicDescriptions, type CliDocsArgs, type CliDocsResult, type ToolResult, toMcpResponse, };
39
+ export { validateXanoscript, TYPE_ALIASES, RESERVED_VARIABLES, type ValidateXanoscriptArgs, type ValidationResult, type BatchValidationResult, type SingleFileValidationResult, type ParserDiagnostic, xanoscriptDocs, getXanoscriptDocsPath, setXanoscriptDocsPath, type XanoscriptDocsArgs, type XanoscriptDocsResult, mcpVersion, getServerVersion, setServerVersion, type McpVersionResult, metaApiDocs, metaApiTopics, getMetaApiTopicNames, getMetaApiTopicDescriptions, type MetaApiDocsArgs, type MetaApiDocsResult, runApiDocs, runApiTopics, getRunApiTopicNames, getRunApiTopicDescriptions, type RunApiDocsArgs, type RunApiDocsResult, cliDocs, cliTopics, getCliTopicNames, getCliTopicDescriptions, type CliDocsArgs, type CliDocsResult, type ToolResult, toMcpResponse, };
40
40
  export { validateXanoscriptTool, xanoscriptDocsTool, mcpVersionTool, metaApiDocsTool, runApiDocsTool, cliDocsTool, };
41
41
  export { validateXanoscriptToolDefinition, xanoscriptDocsToolDefinition, mcpVersionToolDefinition, metaApiDocsToolDefinition, runApiDocsToolDefinition, cliDocsToolDefinition, };
42
42
  /**
43
43
  * All tool definitions for MCP server registration
44
44
  */
45
45
  export declare const toolDefinitions: ({
46
- name: string;
47
- description: string;
48
- inputSchema: {
49
- type: string;
50
- properties: {
51
- code: {
52
- type: string;
53
- description: string;
54
- };
55
- };
56
- required: string[];
57
- };
58
- } | {
59
46
  name: string;
60
47
  description: string;
61
48
  inputSchema: {
@@ -32,7 +32,7 @@
32
32
  // =============================================================================
33
33
  // Tool Imports
34
34
  // =============================================================================
35
- import { validateXanoscript, validateXanoscriptTool, validateXanoscriptToolDefinition, } from "./validate_xanoscript.js";
35
+ import { validateXanoscript, validateXanoscriptTool, validateXanoscriptToolDefinition, TYPE_ALIASES, RESERVED_VARIABLES, } from "./validate_xanoscript.js";
36
36
  import { xanoscriptDocs, xanoscriptDocsTool, xanoscriptDocsToolDefinition, getXanoscriptDocsPath, setXanoscriptDocsPath, } from "./xanoscript_docs.js";
37
37
  import { mcpVersion, mcpVersionTool, mcpVersionToolDefinition, getServerVersion, setServerVersion, } from "./mcp_version.js";
38
38
  import { metaApiDocs, metaApiDocsTool, metaApiDocsToolDefinition, metaApiTopics, getMetaApiTopicNames, getMetaApiTopicDescriptions, } from "./meta_api_docs.js";
@@ -44,7 +44,7 @@ import { toMcpResponse } from "./types.js";
44
44
  // =============================================================================
45
45
  export {
46
46
  // Validation
47
- validateXanoscript,
47
+ validateXanoscript, TYPE_ALIASES, RESERVED_VARIABLES,
48
48
  // XanoScript Documentation
49
49
  xanoscriptDocs, getXanoscriptDocsPath, setXanoscriptDocsPath,
50
50
  // MCP Version
@@ -3,11 +3,25 @@
3
3
  *
4
4
  * Validates XanoScript code for syntax errors using the XanoScript language server.
5
5
  * Can be used standalone or within the MCP server.
6
+ *
7
+ * Supports multiple input methods:
8
+ * - code: Raw XanoScript code as a string
9
+ * - file_path: Path to a single .xs file
10
+ * - file_paths: Array of file paths for batch validation
11
+ * - directory: Directory path with optional glob pattern
6
12
  */
7
13
  import type { ToolResult } from "./types.js";
8
14
  export interface ValidateXanoscriptArgs {
9
- /** The XanoScript code to validate */
10
- code: string;
15
+ /** The XanoScript code to validate (mutually exclusive with file_path/file_paths/directory) */
16
+ code?: string;
17
+ /** Path to a single XanoScript file to validate */
18
+ file_path?: string;
19
+ /** Array of file paths for batch validation */
20
+ file_paths?: string[];
21
+ /** Directory to validate (validates all .xs files recursively) */
22
+ directory?: string;
23
+ /** Glob pattern to filter files when using directory (default: "**\/*.xs") */
24
+ pattern?: string;
11
25
  }
12
26
  export interface ParserDiagnostic {
13
27
  range: {
@@ -23,14 +37,42 @@ export interface ParserDiagnostic {
23
37
  message: string;
24
38
  source: string;
25
39
  }
40
+ export interface SingleFileValidationResult {
41
+ valid: boolean;
42
+ errors: ParserDiagnostic[];
43
+ message: string;
44
+ file_path?: string;
45
+ }
26
46
  export interface ValidationResult {
27
47
  valid: boolean;
28
48
  errors: ParserDiagnostic[];
29
49
  message: string;
30
50
  }
51
+ export interface BatchValidationResult {
52
+ valid: boolean;
53
+ total_files: number;
54
+ valid_files: number;
55
+ invalid_files: number;
56
+ results: SingleFileValidationResult[];
57
+ message: string;
58
+ }
59
+ /**
60
+ * Common type aliases that users might try
61
+ */
62
+ declare const TYPE_ALIASES: Record<string, string>;
63
+ /**
64
+ * Reserved variable names that cannot be used
65
+ */
66
+ declare const RESERVED_VARIABLES: string[];
31
67
  /**
32
68
  * Validate XanoScript code for syntax errors.
33
69
  *
70
+ * Supports multiple input methods:
71
+ * - code: Raw XanoScript code as a string
72
+ * - file_path: Path to a single .xs file
73
+ * - file_paths: Array of file paths for batch validation
74
+ * - directory: Directory path with optional glob pattern
75
+ *
34
76
  * @param args - The validation arguments
35
77
  * @returns Validation result with errors if any
36
78
  *
@@ -38,15 +80,28 @@ export interface ValidationResult {
38
80
  * ```ts
39
81
  * import { validateXanoscript } from '@xano/developer-mcp';
40
82
  *
83
+ * // Validate code directly
41
84
  * const result = validateXanoscript({ code: 'return 1 + 1' });
42
- * if (result.valid) {
43
- * console.log('Code is valid!');
44
- * } else {
45
- * console.log('Errors:', result.errors);
46
- * }
85
+ *
86
+ * // Validate a single file
87
+ * const fileResult = validateXanoscript({ file_path: './functions/utils.xs' });
88
+ *
89
+ * // Validate multiple files
90
+ * const batchResult = validateXanoscript({
91
+ * file_paths: ['./apis/users/get.xs', './apis/users/create.xs']
92
+ * });
93
+ *
94
+ * // Validate all .xs files in a directory
95
+ * const dirResult = validateXanoscript({ directory: './src' });
96
+ *
97
+ * // Validate with a specific pattern
98
+ * const patternResult = validateXanoscript({
99
+ * directory: './src',
100
+ * pattern: 'apis/**\/*.xs'
101
+ * });
47
102
  * ```
48
103
  */
49
- export declare function validateXanoscript(args: ValidateXanoscriptArgs): ValidationResult;
104
+ export declare function validateXanoscript(args: ValidateXanoscriptArgs): ValidationResult | BatchValidationResult;
50
105
  /**
51
106
  * Validate XanoScript and return a simplified result.
52
107
  * Returns ToolResult format for consistent error handling.
@@ -62,7 +117,27 @@ export declare const validateXanoscriptToolDefinition: {
62
117
  type: string;
63
118
  description: string;
64
119
  };
120
+ file_path: {
121
+ type: string;
122
+ description: string;
123
+ };
124
+ file_paths: {
125
+ type: string;
126
+ items: {
127
+ type: string;
128
+ };
129
+ description: string;
130
+ };
131
+ directory: {
132
+ type: string;
133
+ description: string;
134
+ };
135
+ pattern: {
136
+ type: string;
137
+ description: string;
138
+ };
65
139
  };
66
- required: string[];
140
+ required: never[];
67
141
  };
68
142
  };
143
+ export { TYPE_ALIASES, RESERVED_VARIABLES };
@@ -3,47 +3,183 @@
3
3
  *
4
4
  * Validates XanoScript code for syntax errors using the XanoScript language server.
5
5
  * Can be used standalone or within the MCP server.
6
+ *
7
+ * Supports multiple input methods:
8
+ * - code: Raw XanoScript code as a string
9
+ * - file_path: Path to a single .xs file
10
+ * - file_paths: Array of file paths for batch validation
11
+ * - directory: Directory path with optional glob pattern
6
12
  */
7
13
  import { xanoscriptParser } from "@xano/xanoscript-language-server/parser/parser.js";
8
14
  import { getSchemeFromContent } from "@xano/xanoscript-language-server/utils.js";
15
+ import { readFileSync, existsSync, readdirSync } from "fs";
16
+ import { join, resolve, basename } from "path";
17
+ import { minimatch } from "minimatch";
9
18
  // =============================================================================
10
- // Standalone Tool Function
19
+ // Error Message Improvements
11
20
  // =============================================================================
12
21
  /**
13
- * Validate XanoScript code for syntax errors.
14
- *
15
- * @param args - The validation arguments
16
- * @returns Validation result with errors if any
17
- *
18
- * @example
19
- * ```ts
20
- * import { validateXanoscript } from '@xano/developer-mcp';
21
- *
22
- * const result = validateXanoscript({ code: 'return 1 + 1' });
23
- * if (result.valid) {
24
- * console.log('Code is valid!');
25
- * } else {
26
- * console.log('Errors:', result.errors);
27
- * }
28
- * ```
22
+ * Common type aliases that users might try
29
23
  */
30
- export function validateXanoscript(args) {
31
- if (!args?.code) {
32
- return {
33
- valid: false,
34
- errors: [],
35
- message: "Error: 'code' parameter is required",
36
- };
24
+ const TYPE_ALIASES = {
25
+ boolean: "bool",
26
+ integer: "int",
27
+ string: "text",
28
+ number: "decimal",
29
+ float: "decimal",
30
+ double: "decimal",
31
+ array: "type[]",
32
+ list: "type[]",
33
+ object: "json",
34
+ map: "json",
35
+ dict: "json",
36
+ dictionary: "json",
37
+ };
38
+ /**
39
+ * Reserved variable names that cannot be used
40
+ */
41
+ const RESERVED_VARIABLES = [
42
+ "$response",
43
+ "$output",
44
+ "$input",
45
+ "$auth",
46
+ "$env",
47
+ "$db",
48
+ "$this",
49
+ "$result",
50
+ ];
51
+ /**
52
+ * Common syntax mistakes and their fixes
53
+ */
54
+ const SYNTAX_SUGGESTIONS = [
55
+ {
56
+ pattern: /else\s+if/,
57
+ suggestion: 'Use "elseif" (one word) instead of "else if"',
58
+ },
59
+ {
60
+ pattern: /body\s*=/,
61
+ suggestion: 'Use "params" instead of "body" for api.request request body',
62
+ },
63
+ {
64
+ pattern: /\|default:/,
65
+ suggestion: 'There is no "default" filter. Use "first_notnull" or "??" operator instead',
66
+ },
67
+ {
68
+ pattern: /boolean/,
69
+ suggestion: 'Use "bool" instead of "boolean" for type declaration',
70
+ },
71
+ {
72
+ pattern: /integer(?!\s*\()/,
73
+ suggestion: 'Use "int" instead of "integer" for type declaration',
74
+ },
75
+ {
76
+ pattern: /string(?!\s*\()/,
77
+ suggestion: 'Use "text" instead of "string" for type declaration',
78
+ },
79
+ ];
80
+ /**
81
+ * Enhance error message with helpful suggestions
82
+ */
83
+ function enhanceErrorMessage(message, code, lineNumber) {
84
+ let enhanced = message;
85
+ const lines = code.split("\n");
86
+ const errorLine = lines[lineNumber] || "";
87
+ // Check for type aliases
88
+ for (const [alias, correct] of Object.entries(TYPE_ALIASES)) {
89
+ const regex = new RegExp(`\\b${alias}\\b`, "i");
90
+ if (regex.test(errorLine)) {
91
+ enhanced += `\n\n💡 Suggestion: Use "${correct}" instead of "${alias}"`;
92
+ break;
93
+ }
94
+ }
95
+ // Check for reserved variables
96
+ for (const reserved of RESERVED_VARIABLES) {
97
+ if (errorLine.includes(`var ${reserved}`) ||
98
+ errorLine.includes(`var.update ${reserved}`)) {
99
+ enhanced += `\n\n💡 "${reserved}" is a reserved variable name. Try a different name like "${reserved.replace("$", "$my_")}"`;
100
+ break;
101
+ }
102
+ }
103
+ // Check for common syntax mistakes
104
+ for (const { pattern, suggestion } of SYNTAX_SUGGESTIONS) {
105
+ if (pattern.test(errorLine) || pattern.test(code)) {
106
+ enhanced += `\n\n💡 Suggestion: ${suggestion}`;
107
+ break;
108
+ }
109
+ }
110
+ // Add the actual line of code for context
111
+ if (errorLine.trim()) {
112
+ enhanced += `\n\nCode at line ${lineNumber + 1}:\n ${errorLine.trim()}`;
113
+ }
114
+ return enhanced;
115
+ }
116
+ // =============================================================================
117
+ // File Reading Utilities
118
+ // =============================================================================
119
+ /**
120
+ * Read a file and return its contents
121
+ */
122
+ function readFile(filePath) {
123
+ try {
124
+ const absolutePath = resolve(filePath);
125
+ if (!existsSync(absolutePath)) {
126
+ return { content: "", error: `File not found: ${filePath}` };
127
+ }
128
+ const content = readFileSync(absolutePath, "utf-8");
129
+ return { content };
130
+ }
131
+ catch (error) {
132
+ const errorMessage = error instanceof Error ? error.message : String(error);
133
+ return { content: "", error: `Error reading file: ${errorMessage}` };
134
+ }
135
+ }
136
+ /**
137
+ * Find all .xs files in a directory matching the pattern
138
+ */
139
+ function findXsFiles(directory, pattern = "**/*.xs") {
140
+ const absoluteDir = resolve(directory);
141
+ if (!existsSync(absoluteDir)) {
142
+ return [];
143
+ }
144
+ const files = [];
145
+ function walkDir(dir, basePath = "") {
146
+ const entries = readdirSync(dir, { withFileTypes: true });
147
+ for (const entry of entries) {
148
+ const fullPath = join(dir, entry.name);
149
+ const relativePath = basePath ? join(basePath, entry.name) : entry.name;
150
+ if (entry.isDirectory()) {
151
+ walkDir(fullPath, relativePath);
152
+ }
153
+ else if (entry.isFile() && entry.name.endsWith(".xs")) {
154
+ if (minimatch(relativePath, pattern)) {
155
+ files.push(fullPath);
156
+ }
157
+ }
158
+ }
37
159
  }
160
+ walkDir(absoluteDir);
161
+ return files;
162
+ }
163
+ // =============================================================================
164
+ // Standalone Tool Function
165
+ // =============================================================================
166
+ /**
167
+ * Validate a single piece of XanoScript code.
168
+ * Internal function used by the public API.
169
+ */
170
+ function validateCode(code, filePath) {
38
171
  try {
39
- const text = args.code;
172
+ const text = code;
40
173
  const scheme = getSchemeFromContent(text);
41
174
  const parser = xanoscriptParser(text, scheme);
42
175
  if (parser.errors.length === 0) {
43
176
  return {
44
177
  valid: true,
45
178
  errors: [],
46
- message: "XanoScript is valid. No syntax errors found.",
179
+ message: filePath
180
+ ? `✓ ${basename(filePath)}: Valid`
181
+ : "XanoScript is valid. No syntax errors found.",
182
+ file_path: filePath,
47
183
  };
48
184
  }
49
185
  const diagnostics = parser.errors.map((error) => {
@@ -55,12 +191,14 @@ export function validateXanoscript(args) {
55
191
  const endLines = text.substring(0, endOffset + 1).split("\n");
56
192
  const endLine = endLines.length - 1;
57
193
  const endCharacter = endLines[endLines.length - 1].length;
194
+ // Enhance error message with suggestions
195
+ const enhancedMessage = enhanceErrorMessage(error.message, text, line);
58
196
  return {
59
197
  range: {
60
198
  start: { line, character },
61
199
  end: { line: endLine, character: endCharacter },
62
200
  },
63
- message: error.message,
201
+ message: enhancedMessage,
64
202
  source: error.name || "XanoScript Parser",
65
203
  };
66
204
  });
@@ -68,10 +206,12 @@ export function validateXanoscript(args) {
68
206
  const location = `Line ${d.range.start.line + 1}, Column ${d.range.start.character + 1}`;
69
207
  return `${i + 1}. [${location}] ${d.message}`;
70
208
  });
209
+ const prefix = filePath ? `✗ ${basename(filePath)}: ` : "";
71
210
  return {
72
211
  valid: false,
73
212
  errors: diagnostics,
74
- message: `Found ${diagnostics.length} error(s):\n\n${errorMessages.join("\n")}`,
213
+ message: `${prefix}Found ${diagnostics.length} error(s):\n\n${errorMessages.join("\n")}`,
214
+ file_path: filePath,
75
215
  };
76
216
  }
77
217
  catch (error) {
@@ -80,9 +220,156 @@ export function validateXanoscript(args) {
80
220
  valid: false,
81
221
  errors: [],
82
222
  message: `Validation error: ${errorMessage}`,
223
+ file_path: filePath,
83
224
  };
84
225
  }
85
226
  }
227
+ /**
228
+ * Validate XanoScript code for syntax errors.
229
+ *
230
+ * Supports multiple input methods:
231
+ * - code: Raw XanoScript code as a string
232
+ * - file_path: Path to a single .xs file
233
+ * - file_paths: Array of file paths for batch validation
234
+ * - directory: Directory path with optional glob pattern
235
+ *
236
+ * @param args - The validation arguments
237
+ * @returns Validation result with errors if any
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * import { validateXanoscript } from '@xano/developer-mcp';
242
+ *
243
+ * // Validate code directly
244
+ * const result = validateXanoscript({ code: 'return 1 + 1' });
245
+ *
246
+ * // Validate a single file
247
+ * const fileResult = validateXanoscript({ file_path: './functions/utils.xs' });
248
+ *
249
+ * // Validate multiple files
250
+ * const batchResult = validateXanoscript({
251
+ * file_paths: ['./apis/users/get.xs', './apis/users/create.xs']
252
+ * });
253
+ *
254
+ * // Validate all .xs files in a directory
255
+ * const dirResult = validateXanoscript({ directory: './src' });
256
+ *
257
+ * // Validate with a specific pattern
258
+ * const patternResult = validateXanoscript({
259
+ * directory: './src',
260
+ * pattern: 'apis/**\/*.xs'
261
+ * });
262
+ * ```
263
+ */
264
+ export function validateXanoscript(args) {
265
+ // Validate that at least one input method is provided
266
+ if (!args?.code && !args?.file_path && !args?.file_paths && !args?.directory) {
267
+ return {
268
+ valid: false,
269
+ errors: [],
270
+ message: "Error: One of 'code', 'file_path', 'file_paths', or 'directory' parameter is required",
271
+ };
272
+ }
273
+ // Handle direct code validation
274
+ if (args.code) {
275
+ const result = validateCode(args.code);
276
+ return {
277
+ valid: result.valid,
278
+ errors: result.errors,
279
+ message: result.message,
280
+ };
281
+ }
282
+ // Handle single file validation
283
+ if (args.file_path) {
284
+ const { content, error } = readFile(args.file_path);
285
+ if (error) {
286
+ return {
287
+ valid: false,
288
+ errors: [],
289
+ message: error,
290
+ };
291
+ }
292
+ const result = validateCode(content, args.file_path);
293
+ return {
294
+ valid: result.valid,
295
+ errors: result.errors,
296
+ message: result.message,
297
+ };
298
+ }
299
+ // Handle batch validation (file_paths or directory)
300
+ let filesToValidate = [];
301
+ if (args.file_paths) {
302
+ filesToValidate = args.file_paths;
303
+ }
304
+ else if (args.directory) {
305
+ filesToValidate = findXsFiles(args.directory, args.pattern || "**/*.xs");
306
+ if (filesToValidate.length === 0) {
307
+ return {
308
+ valid: true,
309
+ total_files: 0,
310
+ valid_files: 0,
311
+ invalid_files: 0,
312
+ results: [],
313
+ message: `No .xs files found in directory: ${args.directory}${args.pattern ? ` matching pattern: ${args.pattern}` : ""}`,
314
+ };
315
+ }
316
+ }
317
+ // Validate all files
318
+ const results = [];
319
+ let validCount = 0;
320
+ let invalidCount = 0;
321
+ for (const filePath of filesToValidate) {
322
+ const { content, error } = readFile(filePath);
323
+ if (error) {
324
+ results.push({
325
+ valid: false,
326
+ errors: [],
327
+ message: error,
328
+ file_path: filePath,
329
+ });
330
+ invalidCount++;
331
+ continue;
332
+ }
333
+ const result = validateCode(content, filePath);
334
+ results.push(result);
335
+ if (result.valid) {
336
+ validCount++;
337
+ }
338
+ else {
339
+ invalidCount++;
340
+ }
341
+ }
342
+ // Build summary message
343
+ const allValid = invalidCount === 0;
344
+ const summaryLines = [
345
+ `Validated ${filesToValidate.length} file(s): ${validCount} valid, ${invalidCount} invalid`,
346
+ "",
347
+ ];
348
+ // Show errors first, then valid files
349
+ const invalidResults = results.filter((r) => !r.valid);
350
+ const validResults = results.filter((r) => r.valid);
351
+ if (invalidResults.length > 0) {
352
+ summaryLines.push("❌ Files with errors:");
353
+ for (const result of invalidResults) {
354
+ summaryLines.push(`\n${result.message}`);
355
+ }
356
+ summaryLines.push("");
357
+ }
358
+ if (validResults.length > 0) {
359
+ summaryLines.push("✅ Valid files:");
360
+ for (const result of validResults) {
361
+ summaryLines.push(` ${result.file_path}`);
362
+ }
363
+ }
364
+ return {
365
+ valid: allValid,
366
+ total_files: filesToValidate.length,
367
+ valid_files: validCount,
368
+ invalid_files: invalidCount,
369
+ results,
370
+ message: summaryLines.join("\n"),
371
+ };
372
+ }
86
373
  /**
87
374
  * Validate XanoScript and return a simplified result.
88
375
  * Returns ToolResult format for consistent error handling.
@@ -100,15 +387,42 @@ export function validateXanoscriptTool(args) {
100
387
  // =============================================================================
101
388
  export const validateXanoscriptToolDefinition = {
102
389
  name: "validate_xanoscript",
103
- 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.",
390
+ description: "Validate XanoScript code for syntax errors. Supports multiple input methods:\n" +
391
+ "- code: Raw XanoScript code as a string\n" +
392
+ "- file_path: Path to a single .xs file (easier than escaping code!)\n" +
393
+ "- file_paths: Array of file paths for batch validation\n" +
394
+ "- directory: Validate all .xs files in a directory\n\n" +
395
+ "Returns errors with line/column positions and helpful suggestions for common mistakes. " +
396
+ "The language server auto-detects the object type from the code syntax.",
104
397
  inputSchema: {
105
398
  type: "object",
106
399
  properties: {
107
400
  code: {
108
401
  type: "string",
109
- description: "The XanoScript code to validate",
402
+ description: "The XanoScript code to validate as a string. Use file_path instead if the code contains special characters that are hard to escape.",
403
+ },
404
+ file_path: {
405
+ type: "string",
406
+ description: "Path to a single XanoScript file to validate. Easier than passing code directly - avoids escaping issues.",
407
+ },
408
+ file_paths: {
409
+ type: "array",
410
+ items: { type: "string" },
411
+ description: "Array of file paths for batch validation. Returns a summary with per-file results.",
412
+ },
413
+ directory: {
414
+ type: "string",
415
+ description: "Directory path to validate. Validates all .xs files recursively. Use with 'pattern' to filter.",
416
+ },
417
+ pattern: {
418
+ type: "string",
419
+ description: 'Glob pattern to filter files when using directory (default: "**/*.xs"). Example: "apis/**/*.xs"',
110
420
  },
111
421
  },
112
- required: ["code"],
422
+ required: [],
113
423
  },
114
424
  };
425
+ // =============================================================================
426
+ // Utility Exports
427
+ // =============================================================================
428
+ export { TYPE_ALIASES, RESERVED_VARIABLES };
@@ -108,7 +108,20 @@ $db.table.field // Database field reference (in queries)
108
108
  $this // Current item in loops/maps
109
109
  ```
110
110
 
111
- **Note:** `$response` is a reserved word and cannot be used as a variable name.
111
+ **Reserved Variables:** The following cannot be used as variable names: `$response`, `$output`, `$input`, `$auth`, `$env`, `$db`, `$this`, `$result`.
112
+
113
+ ### Type Names
114
+
115
+ XanoScript uses specific type names:
116
+
117
+ | Type | Description | Example |
118
+ |------|-------------|---------|
119
+ | `text` | String (not "string") | `text name` |
120
+ | `int` | Integer (not "integer") | `int count` |
121
+ | `bool` | Boolean (not "boolean") | `bool active` |
122
+ | `decimal` | Float/number | `decimal price` |
123
+ | `type[]` | Array (not "array" or "list") | `text[] tags` |
124
+ | `json` | Arbitrary JSON data | `json metadata` |
112
125
 
113
126
  ### Comments
114
127
 
@@ -612,3 +612,17 @@ try_catch {
612
612
  5. **Use null-safe operators** - `==?` for optional filters
613
613
  6. **Use bulk operations for batch processing** - More efficient than loops
614
614
  7. **Handle deadlocks gracefully** - Implement retry logic for concurrent writes
615
+
616
+ ---
617
+
618
+ ## Related Topics
619
+
620
+ Explore more with `xanoscript_docs({ topic: "<topic>" })`:
621
+
622
+ | Topic | Description |
623
+ |-------|-------------|
624
+ | `tables` | Database schema definitions with indexes and relationships |
625
+ | `syntax` | Query filters, operators, and expressions |
626
+ | `quickstart` | Common CRUD patterns and examples |
627
+ | `addons` | Reusable subqueries for fetching related data |
628
+ | `performance` | Query optimization best practices |
@@ -465,3 +465,17 @@ foreach ($items) {
465
465
  5. **Keep stacks shallow** - Avoid deeply nested conditionals
466
466
  6. **Use group for organization** - Visually group related statements for readability
467
467
  7. **Use remove sparingly** - Consider filtering arrays instead
468
+
469
+ ---
470
+
471
+ ## Related Topics
472
+
473
+ Explore more with `xanoscript_docs({ topic: "<topic>" })`:
474
+
475
+ | Topic | Description |
476
+ |-------|-------------|
477
+ | `types` | Input types, filters, and validation |
478
+ | `syntax` | Expressions, operators, and control flow |
479
+ | `quickstart` | Common patterns and examples |
480
+ | `database` | Database operations in function stacks |
481
+ | `testing` | Unit testing functions |
@@ -8,6 +8,62 @@ Essential patterns for XanoScript development. Use this as a quick reference for
8
8
 
9
9
  ## Quick Reference
10
10
 
11
+ ### Reserved Variable Names
12
+
13
+ These variable names are reserved and cannot be used:
14
+
15
+ | Variable | Description |
16
+ |----------|-------------|
17
+ | `$response` | API/function response (auto-populated) |
18
+ | `$output` | Output value |
19
+ | `$input` | Input parameters from request/function call |
20
+ | `$auth` | Authenticated user context |
21
+ | `$env` | Environment variables and request context |
22
+ | `$db` | Database table reference for queries |
23
+ | `$this` | Current context reference |
24
+ | `$result` | Used in reduce operations |
25
+
26
+ ```xs
27
+ // ❌ Wrong - using reserved variable name
28
+ var $response { value = "test" } // Error: $response is reserved
29
+
30
+ // ✅ Correct - use a different name
31
+ var $api_response { value = "test" }
32
+ var $my_result { value = "test" }
33
+ ```
34
+
35
+ ### Type Names (Common Aliases)
36
+
37
+ XanoScript uses specific type names. Common aliases from other languages won't work.
38
+
39
+ > **Full reference:** For complete type details and validation, see `xanoscript_docs({ topic: "types" })`.
40
+
41
+ | ❌ Wrong | ✅ Correct | Description |
42
+ |----------|------------|-------------|
43
+ | `boolean` | `bool` | Boolean true/false |
44
+ | `integer` | `int` | 32-bit integer |
45
+ | `string` | `text` | UTF-8 string |
46
+ | `number` | `decimal` | Floating-point number |
47
+ | `float` | `decimal` | Floating-point number |
48
+ | `array` | `type[]` | Array (e.g., `text[]`, `int[]`) |
49
+ | `list` | `type[]` | Array (e.g., `text[]`, `int[]`) |
50
+
51
+ ```xs
52
+ // ❌ Wrong - invalid type names
53
+ input {
54
+ boolean is_active // Error: use "bool"
55
+ integer count // Error: use "int"
56
+ string name // Error: use "text"
57
+ }
58
+
59
+ // ✅ Correct - proper XanoScript types
60
+ input {
61
+ bool is_active
62
+ int count
63
+ text name
64
+ }
65
+ ```
66
+
11
67
  ### Variable Declaration
12
68
  ```xs
13
69
  var $name { value = "initial value" }
@@ -35,7 +91,7 @@ conditional {
35
91
  api.request {
36
92
  url = "https://api.example.com/data"
37
93
  method = "POST"
38
- params = $payload
94
+ params = $payload // Note: "params" is used for request body, NOT "body"
39
95
  headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
40
96
  } as $api_result
41
97
 
@@ -47,6 +103,27 @@ precondition ($api_result.response.status == 200) {
47
103
  var $data { value = $api_result.response.result }
48
104
  ```
49
105
 
106
+ ### api.request Response Structure
107
+ The response object contains:
108
+ ```xs
109
+ $result.response.status // HTTP status code (200, 404, 500, etc.)
110
+ $result.response.result // Parsed response body (JSON decoded)
111
+ $result.response.headers // Response headers object
112
+ ```
113
+
114
+ ### While Loop
115
+ ```xs
116
+ stack {
117
+ var $counter { value = 0 }
118
+ while ($counter < 10) {
119
+ each {
120
+ var.update $counter { value = $counter + 1 }
121
+ debug.log { value = "Iteration: " ~ ($counter|to_text) }
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
50
127
  ### String Concatenation
51
128
  ```xs
52
129
  var $greeting { value = "Hello, " ~ $input.name ~ "!" }
@@ -55,6 +132,116 @@ var $greeting { value = "Hello, " ~ $input.name ~ "!" }
55
132
  var $message { value = "Status: " ~ ($status|to_text) ~ " - " ~ ($data|json_encode) }
56
133
  ```
57
134
 
135
+ ### Input Block Syntax
136
+
137
+ > **Full reference:** For complete input types and validation options, see `xanoscript_docs({ topic: "types" })`.
138
+
139
+ ```xs
140
+ input {
141
+ // Required input
142
+ text name
143
+
144
+ // Optional input (can be omitted)
145
+ text nickname?
146
+
147
+ // Optional with default value
148
+ text role?="user"
149
+
150
+ // With filters applied
151
+ email contact filters=trim|lower
152
+
153
+ // Optional with default AND filters
154
+ text search?="" filters=trim
155
+
156
+ // Array type
157
+ text[] tags filters=trim
158
+
159
+ // Nested object with schema
160
+ object address {
161
+ schema {
162
+ text street
163
+ text city
164
+ text country?="US"
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ ### Error Types Reference
171
+
172
+ > **Full reference:** For try-catch, throw, and preconditions, see `xanoscript_docs({ topic: "syntax" })`.
173
+
174
+ | Type | HTTP Status | Use Case |
175
+ |------|-------------|----------|
176
+ | `inputerror` | 400 Bad Request | Invalid input data |
177
+ | `accessdenied` | 403 Forbidden | Authorization failure |
178
+ | `notfound` | 404 Not Found | Resource doesn't exist |
179
+ | `standard` | 500 Internal Server Error | General errors |
180
+
181
+ ### Quick Filter Reference
182
+
183
+ > **Full reference:** For the complete list of filters with examples, see `xanoscript_docs({ topic: "syntax" })`.
184
+
185
+ **String Filters:**
186
+ ```xs
187
+ $s|trim // Remove whitespace
188
+ $s|to_lower // Lowercase
189
+ $s|to_upper // Uppercase
190
+ $s|substr:1:3 // Substring from index 1, length 3
191
+ $s|split:"," // Split by delimiter → array
192
+ $s|replace:"old":"new" // Replace text
193
+ $s|contains:"text" // Check if contains → bool
194
+ $s|strlen // String length
195
+ ```
196
+
197
+ **Array Filters:**
198
+ ```xs
199
+ $arr|first // First element
200
+ $arr|last // Last element
201
+ $arr|count // Array length
202
+ $arr|push:$item // Add to end
203
+ $arr|pop // Remove & return last
204
+ $arr|join:"," // Join to string
205
+ $arr|map:$$.name // Transform elements
206
+ $arr|filter:$$.active // Filter elements
207
+ $arr|find:$$.id == 5 // Find first match
208
+ ```
209
+
210
+ **Object Filters:**
211
+ ```xs
212
+ $obj|get:"key" // Get property value
213
+ $obj|get:"key":"default" // With default if missing
214
+ $obj|set:"key":"value" // Set property
215
+ $obj|has:"key" // Check if key exists → bool
216
+ $obj|keys // Get all keys → array
217
+ $obj|values // Get all values → array
218
+ ```
219
+
220
+ **Type Conversion:**
221
+ ```xs
222
+ $val|to_text // Convert to string
223
+ $val|to_int // Convert to integer
224
+ $val|to_decimal // Convert to decimal
225
+ $val|to_bool // Convert to boolean
226
+ $val|json_encode // Object → JSON string
227
+ $str|json_decode // JSON string → object
228
+ ```
229
+
230
+ **Null Handling:**
231
+ ```xs
232
+ $val|first_notnull:"default" // Default if null
233
+ $val|first_notempty:"default" // Default if empty
234
+ $val ?? "default" // Nullish coalescing (same as first_notnull)
235
+ ```
236
+
237
+ **Encoding:**
238
+ ```xs
239
+ $s|url_encode // URL encode
240
+ $s|url_decode // URL decode
241
+ $s|base64_encode // Base64 encode
242
+ $s|base64_decode // Base64 decode
243
+ ```
244
+
58
245
  ---
59
246
 
60
247
  ## Common Patterns
@@ -77,6 +264,8 @@ precondition ($input.email|contains:"@") {
77
264
 
78
265
  ### 2. Database CRUD
79
266
 
267
+ > **Full reference:** For all db.* operations including transactions, see `xanoscript_docs({ topic: "database" })`.
268
+
80
269
  ```xs
81
270
  // Create
82
271
  db.add "user" {
@@ -142,6 +331,8 @@ var $active_items { value = $items|filter:$$.is_active == true }
142
331
 
143
332
  ### 5. Error Handling with Try-Catch
144
333
 
334
+ > **Full reference:** For all error handling patterns, see `xanoscript_docs({ topic: "syntax" })`.
335
+
145
336
  ```xs
146
337
  try_catch {
147
338
  try {
@@ -159,6 +350,8 @@ try_catch {
159
350
 
160
351
  ### 6. Authentication Check
161
352
 
353
+ > **Full reference:** For security best practices, see `xanoscript_docs({ topic: "security" })`.
354
+
162
355
  ```xs
163
356
  // Require authenticated user
164
357
  precondition ($auth.id != null) {
@@ -220,6 +413,8 @@ var $response {
220
413
 
221
414
  ### 9. Date/Time Operations
222
415
 
416
+ > **Full reference:** For all date/time filters, see `xanoscript_docs({ topic: "syntax" })`.
417
+
223
418
  ```xs
224
419
  // Current timestamp
225
420
  var $now { value = now }
@@ -239,6 +434,8 @@ db.query "event" {
239
434
 
240
435
  ### 10. JSON API Response
241
436
 
437
+ > **Full reference:** For external API patterns, see `xanoscript_docs({ topic: "integrations" })`.
438
+
242
439
  ```xs
243
440
  api.request {
244
441
  url = "https://api.openai.com/v1/chat/completions"
@@ -287,42 +484,104 @@ conditional {
287
484
 
288
485
  ### 2. Missing parentheses in filter concatenation
289
486
  ```xs
290
- // ❌ Wrong
487
+ // ❌ Wrong - parse error
291
488
  var $msg { value = $status|to_text ~ " - " ~ $data|json_encode }
292
489
 
293
- // ✅ Correct
490
+ // ✅ Correct - wrap filtered expressions in parentheses
294
491
  var $msg { value = ($status|to_text) ~ " - " ~ ($data|json_encode) }
295
492
  ```
296
493
 
297
- ### 3. Using `body` instead of `params` for api.request
494
+ ### 3. Missing parentheses in filter comparisons
298
495
  ```xs
299
- // ❌ Wrong
496
+ // ❌ Wrong - parse error
497
+ if ($array|count > 0) { }
498
+
499
+ // ✅ Correct - wrap filter expression in parentheses
500
+ if (($array|count) > 0) { }
501
+ ```
502
+
503
+ ### 4. Using `body` instead of `params` for api.request
504
+ ```xs
505
+ // ❌ Wrong - "body" is not valid
300
506
  api.request {
301
507
  url = "..."
302
508
  method = "POST"
303
- body = $payload // "body" is not valid!
509
+ body = $payload
304
510
  }
305
511
 
306
- // ✅ Correct
512
+ // ✅ Correct - use "params" for request body
307
513
  api.request {
308
514
  url = "..."
309
515
  method = "POST"
310
- params = $payload // Use "params" for request body
516
+ params = $payload
311
517
  }
312
518
  ```
313
519
 
314
- ### 4. Using `default` filter (doesn't exist)
520
+ ### 5. Using `default` filter (doesn't exist)
315
521
  ```xs
316
- // ❌ Wrong
522
+ // ❌ Wrong - no "default" filter exists
317
523
  var $value { value = $input.optional|default:"fallback" }
318
524
 
319
- // ✅ Correct
525
+ // ✅ Correct - use first_notnull or ?? operator
320
526
  var $value { value = $input.optional|first_notnull:"fallback" }
321
527
  // or
322
528
  var $value { value = $input.optional ?? "fallback" }
529
+
530
+ // For object key access with default, use get with 3rd parameter
531
+ var $val { value = $obj|get:"key":"default_value" }
532
+ ```
533
+
534
+ ### 6. Using reserved variable names
535
+ ```xs
536
+ // ❌ Wrong - $response is reserved
537
+ var $response { value = "test" }
538
+
539
+ // ✅ Correct - use a different name
540
+ var $api_response { value = "test" }
541
+ ```
542
+
543
+ ### 7. Wrong type names
544
+ ```xs
545
+ // ❌ Wrong - invalid type names
546
+ input {
547
+ boolean active // Use "bool"
548
+ integer count // Use "int"
549
+ string name // Use "text"
550
+ }
551
+
552
+ // ✅ Correct
553
+ input {
554
+ bool active
555
+ int count
556
+ text name
557
+ }
558
+ ```
559
+
560
+ ### 8. Object literal syntax (using = instead of :)
561
+ ```xs
562
+ // ❌ Wrong - object literals use : not =
563
+ var $data { value = { customer = $id } }
564
+
565
+ // ✅ Correct - use : for object properties
566
+ var $data { value = { customer: $id } }
567
+ ```
568
+
569
+ ### 9. Throw block with commas
570
+ ```xs
571
+ // ❌ Wrong - throw blocks don't use commas
572
+ throw {
573
+ name = "Error",
574
+ value = "message"
575
+ }
576
+
577
+ // ✅ Correct - no commas between properties
578
+ throw {
579
+ name = "Error"
580
+ value = "message"
581
+ }
323
582
  ```
324
583
 
325
- ### 5. Using $env in run.job input blocks
584
+ ### 10. Using $env in run.job input blocks
326
585
  ```xs
327
586
  // ❌ Wrong - $env not allowed in input blocks
328
587
  run.job "my_job" {
@@ -331,10 +590,64 @@ run.job "my_job" {
331
590
  }
332
591
  }
333
592
 
334
- // ✅ Correct - use $env in the stack
593
+ // ✅ Correct - access $env in the stack instead
335
594
  run.job "my_job" {
336
595
  stack {
337
596
  var $api_key { value = $env.API_KEY }
338
597
  }
339
598
  }
340
599
  ```
600
+
601
+ ### 11. Using `object` type without schema
602
+ ```xs
603
+ // ❌ Wrong - object requires a schema
604
+ input {
605
+ object data // Error: needs schema
606
+ }
607
+
608
+ // ✅ Correct - use json for arbitrary data
609
+ input {
610
+ json data // Accepts any JSON
611
+ }
612
+
613
+ // ✅ Or define a schema for object
614
+ input {
615
+ object data {
616
+ schema {
617
+ text name
618
+ int id
619
+ }
620
+ }
621
+ }
622
+ ```
623
+
624
+ ### 12. While loop outside of stack block
625
+ ```xs
626
+ // ❌ Wrong - while must be inside stack
627
+ while (true) {
628
+ each { ... }
629
+ }
630
+
631
+ // ✅ Correct - wrap in stack block
632
+ stack {
633
+ while (true) {
634
+ each { ... }
635
+ }
636
+ }
637
+ ```
638
+
639
+ ---
640
+
641
+ ## Related Topics
642
+
643
+ Explore more with `xanoscript_docs({ topic: "<topic>" })`:
644
+
645
+ | Topic | Description |
646
+ |-------|-------------|
647
+ | `syntax` | Complete filter reference, operators, system variables |
648
+ | `types` | Data types, input validation, schema definitions |
649
+ | `database` | All db.* operations: query, get, add, edit, delete |
650
+ | `functions` | Reusable function stacks, async patterns, loops |
651
+ | `apis` | HTTP endpoints, authentication, CRUD patterns |
652
+ | `security` | Security best practices and authentication |
653
+ | `integrations` | External API patterns (OpenAI, Stripe, etc.) |
@@ -280,6 +280,8 @@ Generate numeric ranges with the `..` operator:
280
280
 
281
281
  ## Type Filters
282
282
 
283
+ > **Full reference:** For input types and validation, see `xanoscript_docs({ topic: "types" })`.
284
+
283
285
  | Filter | Example | Result |
284
286
  |--------|---------|--------|
285
287
  | `to_int` | `"123"\|to_int` | `123` |
@@ -343,6 +345,8 @@ $ts|timestamp_day_of_week // Day (0=Sunday)
343
345
 
344
346
  ## Security Filters
345
347
 
348
+ > **Full reference:** For security best practices, see `xanoscript_docs({ topic: "security" })`.
349
+
346
350
  | Filter | Example |
347
351
  |--------|---------|
348
352
  | `md5` | `"text"\|md5` |
@@ -359,6 +363,8 @@ $ts|timestamp_day_of_week // Day (0=Sunday)
359
363
 
360
364
  ## DB Query Filters
361
365
 
366
+ > **Full reference:** For complete database operations, see `xanoscript_docs({ topic: "database" })`.
367
+
362
368
  Used in `db.query` where clauses:
363
369
 
364
370
  | Filter | Example | Description |
@@ -731,6 +737,8 @@ $db.created_at|timestamp_epoch_ms // Milliseconds since epoch
731
737
 
732
738
  ### Vector Operations (AI/ML)
733
739
 
740
+ > **Full reference:** For AI agents and embeddings, see `xanoscript_docs({ topic: "agents" })`.
741
+
734
742
  Additional vector similarity functions:
735
743
 
736
744
  ```xs
@@ -741,3 +749,17 @@ $db.embedding|inner_product:$input.vector // Inner product
741
749
  // Geo covers (for polygon containment)
742
750
  $db.boundary|covers:$input.point // Polygon covers point
743
751
  ```
752
+
753
+ ---
754
+
755
+ ## Related Topics
756
+
757
+ Explore more with `xanoscript_docs({ topic: "<topic>" })`:
758
+
759
+ | Topic | Description |
760
+ |-------|-------------|
761
+ | `quickstart` | Common patterns, examples, mistakes to avoid |
762
+ | `types` | Data types, input validation, schema definitions |
763
+ | `database` | All db.* operations with query examples |
764
+ | `functions` | Reusable function stacks, async patterns |
765
+ | `security` | Security best practices and authentication |
@@ -362,3 +362,17 @@ precondition ($input.start_date < $input.end_date) {
362
362
  2. **Use filters first** - Prefer declarative filters over stack validation
363
363
  3. **Mark sensitive data** - Use `sensitive = true` for PII/credentials
364
364
  4. **Validate at boundaries** - Validate user input, trust internal calls
365
+
366
+ ---
367
+
368
+ ## Related Topics
369
+
370
+ Explore more with `xanoscript_docs({ topic: "<topic>" })`:
371
+
372
+ | Topic | Description |
373
+ |-------|-------------|
374
+ | `schema` | Runtime schema parsing and validation |
375
+ | `syntax` | All filters, operators, and error handling |
376
+ | `quickstart` | Common patterns and mistakes to avoid |
377
+ | `functions` | Using input blocks in functions |
378
+ | `apis` | Using input blocks in API endpoints |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/developer-mcp",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "MCP server and library for Xano development - XanoScript validation, Meta API, Run API, and CLI documentation",
5
5
  "type": "module",
6
6
  "main": "dist/lib.js",