@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.
- package/dist/tools/index.d.ts +2 -15
- package/dist/tools/index.js +2 -2
- package/dist/tools/validate_xanoscript.d.ts +84 -9
- package/dist/tools/validate_xanoscript.js +345 -31
- package/dist/xanoscript.js +5 -0
- package/dist/xanoscript.test.js +4 -2
- package/dist/xanoscript_docs/README.md +81 -7
- package/dist/xanoscript_docs/database.md +14 -0
- package/dist/xanoscript_docs/functions.md +14 -0
- package/dist/xanoscript_docs/integrations.md +75 -5
- package/dist/xanoscript_docs/quickstart.md +653 -0
- package/dist/xanoscript_docs/syntax.md +104 -1
- package/dist/xanoscript_docs/types.md +14 -0
- package/package.json +1 -1
package/dist/tools/index.d.ts
CHANGED
|
@@ -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: {
|
package/dist/tools/index.js
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
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:
|
|
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
|
-
//
|
|
19
|
+
// Error Message Improvements
|
|
11
20
|
// =============================================================================
|
|
12
21
|
/**
|
|
13
|
-
*
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
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: [
|
|
422
|
+
required: [],
|
|
113
423
|
},
|
|
114
424
|
};
|
|
425
|
+
// =============================================================================
|
|
426
|
+
// Utility Exports
|
|
427
|
+
// =============================================================================
|
|
428
|
+
export { TYPE_ALIASES, RESERVED_VARIABLES };
|
package/dist/xanoscript.js
CHANGED
|
@@ -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"],
|
package/dist/xanoscript.test.js
CHANGED
|
@@ -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
|
|
139
|
+
it("should include syntax and quickstart for .xs files", () => {
|
|
139
140
|
const result = getDocsForFilePath("some/random/file.xs");
|
|
140
|
-
expect(result
|
|
141
|
+
expect(result).toContain("syntax");
|
|
142
|
+
expect(result).toContain("quickstart");
|
|
141
143
|
});
|
|
142
144
|
});
|
|
143
145
|
describe("extractQuickReference", () => {
|