@xano/developer-mcp 1.0.33 → 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 };
@@ -21,6 +21,11 @@ export const XANOSCRIPT_DOCS_V2 = {
21
21
  applyTo: ["**/*.xs"],
22
22
  description: "Expressions, operators, and filters for all XanoScript code",
23
23
  },
24
+ quickstart: {
25
+ file: "quickstart.md",
26
+ applyTo: ["**/*.xs"],
27
+ description: "Common patterns, quick reference, and common mistakes to avoid",
28
+ },
24
29
  types: {
25
30
  file: "types.md",
26
31
  applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tools/**/*.xs", "agents/**/*.xs"],
@@ -11,6 +11,7 @@ describe("xanoscript module", () => {
11
11
  const expectedTopics = [
12
12
  "readme",
13
13
  "syntax",
14
+ "quickstart",
14
15
  "types",
15
16
  "tables",
16
17
  "functions",
@@ -135,9 +136,10 @@ describe("xanoscript module", () => {
135
136
  const result = getDocsForFilePath("apis/test.xs");
136
137
  expect(result).not.toContain("readme");
137
138
  });
138
- it("should put syntax first if not already matched", () => {
139
+ it("should include syntax and quickstart for .xs files", () => {
139
140
  const result = getDocsForFilePath("some/random/file.xs");
140
- expect(result[0]).toBe("syntax");
141
+ expect(result).toContain("syntax");
142
+ expect(result).toContain("quickstart");
141
143
  });
142
144
  });
143
145
  describe("extractQuickReference", () => {