@xano/developer-mcp 1.0.54 → 1.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +42 -10
  2. package/dist/cli_docs/index.d.ts +10 -0
  3. package/dist/cli_docs/index.js +10 -0
  4. package/dist/index.js +2 -18
  5. package/dist/lib.d.ts +2 -2
  6. package/dist/lib.js +1 -1
  7. package/dist/meta_api_docs/index.d.ts +10 -0
  8. package/dist/meta_api_docs/index.js +10 -0
  9. package/dist/tools/cli_docs.js +1 -0
  10. package/dist/tools/index.d.ts +138 -2
  11. package/dist/tools/index.js +60 -8
  12. package/dist/tools/index.test.d.ts +1 -0
  13. package/dist/tools/index.test.js +179 -0
  14. package/dist/tools/mcp_version.d.ts +10 -0
  15. package/dist/tools/mcp_version.js +13 -1
  16. package/dist/tools/meta_api_docs.js +1 -0
  17. package/dist/tools/types.d.ts +15 -6
  18. package/dist/tools/types.js +10 -4
  19. package/dist/tools/validate_xanoscript.d.ts +14 -0
  20. package/dist/tools/validate_xanoscript.js +22 -5
  21. package/dist/tools/xanoscript_docs.d.ts +32 -1
  22. package/dist/tools/xanoscript_docs.js +69 -5
  23. package/dist/tools/xanoscript_docs.test.d.ts +1 -0
  24. package/dist/tools/xanoscript_docs.test.js +197 -0
  25. package/dist/xanoscript.d.ts +17 -1
  26. package/dist/xanoscript.js +99 -179
  27. package/dist/xanoscript.test.js +84 -8
  28. package/dist/xanoscript_docs/README.md +7 -7
  29. package/dist/xanoscript_docs/cheatsheet.md +4 -1
  30. package/dist/xanoscript_docs/database.md +2 -2
  31. package/dist/xanoscript_docs/docs_index.json +186 -108
  32. package/dist/xanoscript_docs/essentials.md +665 -0
  33. package/dist/xanoscript_docs/functions.md +1 -1
  34. package/dist/xanoscript_docs/middleware.md +5 -18
  35. package/dist/xanoscript_docs/quickstart.md +15 -6
  36. package/dist/xanoscript_docs/security.md +18 -43
  37. package/dist/xanoscript_docs/syntax/array-filters.md +238 -0
  38. package/dist/xanoscript_docs/syntax/functions.md +136 -0
  39. package/dist/xanoscript_docs/syntax/string-filters.md +188 -0
  40. package/dist/xanoscript_docs/syntax.md +92 -900
  41. package/dist/xanoscript_docs/triggers.md +1 -1
  42. package/dist/xanoscript_docs/types.md +1 -1
  43. package/package.json +1 -1
@@ -0,0 +1,179 @@
1
+ import { describe, it, expect, beforeAll } from "vitest";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { existsSync } from "fs";
5
+ import { handleTool, toMcpResponse, toolDefinitions, } from "./index.js";
6
+ import { setXanoscriptDocsPath } from "./xanoscript_docs.js";
7
+ import { XANOSCRIPT_DOCS_V2 } from "../xanoscript.js";
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const DOCS_PATH = join(__dirname, "..", "xanoscript_docs");
11
+ beforeAll(() => {
12
+ setXanoscriptDocsPath(DOCS_PATH);
13
+ });
14
+ describe("handleTool", () => {
15
+ it("should handle xanoscript_docs tool", () => {
16
+ const result = handleTool("xanoscript_docs", {});
17
+ expect(result.success).toBe(true);
18
+ expect(result.data).toBeDefined();
19
+ });
20
+ it("should handle xanoscript_docs with topic arg", () => {
21
+ const result = handleTool("xanoscript_docs", { topic: "syntax" });
22
+ expect(result.success).toBe(true);
23
+ expect(typeof result.data).toBe("string");
24
+ expect(result.data).toContain("Documentation version:");
25
+ });
26
+ it("should handle xanoscript_docs with file_path and return multi-content", () => {
27
+ const result = handleTool("xanoscript_docs", { file_path: "apis/users/create.xs" });
28
+ expect(result.success).toBe(true);
29
+ expect(Array.isArray(result.data)).toBe(true);
30
+ const data = result.data;
31
+ expect(data.length).toBeGreaterThan(1);
32
+ expect(data[0]).toContain("Matched topics:");
33
+ });
34
+ it("should handle validate_xanoscript tool and return a ToolResult", () => {
35
+ const result = handleTool("validate_xanoscript", { code: "var $x:int = 1" });
36
+ expect(result).toHaveProperty("success");
37
+ expect(typeof result.success).toBe("boolean");
38
+ // Either data or error should be present
39
+ expect(result.data ?? result.error).toBeDefined();
40
+ });
41
+ it("should handle validate_xanoscript with missing input", () => {
42
+ const result = handleTool("validate_xanoscript", {});
43
+ expect(result.success).toBe(false);
44
+ expect(result.error).toBeDefined();
45
+ });
46
+ it("should handle mcp_version tool", () => {
47
+ const result = handleTool("mcp_version", {});
48
+ expect(result.success).toBe(true);
49
+ expect(result.data).toBeDefined();
50
+ });
51
+ it("should handle meta_api_docs tool with topic", () => {
52
+ const result = handleTool("meta_api_docs", { topic: "start" });
53
+ expect(result.success).toBe(true);
54
+ expect(result.data).toBeDefined();
55
+ });
56
+ it("should handle meta_api_docs tool without topic", () => {
57
+ const result = handleTool("meta_api_docs", {});
58
+ expect(result.success).toBe(false);
59
+ expect(result.error).toContain("topic");
60
+ });
61
+ it("should handle cli_docs tool with topic", () => {
62
+ const result = handleTool("cli_docs", { topic: "start" });
63
+ expect(result.success).toBe(true);
64
+ expect(result.data).toBeDefined();
65
+ });
66
+ it("should handle cli_docs tool without topic", () => {
67
+ const result = handleTool("cli_docs", {});
68
+ expect(result.success).toBe(false);
69
+ expect(result.error).toContain("topic");
70
+ });
71
+ it("should return error for unknown tool", () => {
72
+ const result = handleTool("nonexistent_tool", {});
73
+ expect(result.success).toBe(false);
74
+ expect(result.error).toContain("Unknown tool: nonexistent_tool");
75
+ });
76
+ it("should return validation error for wrong argument types", () => {
77
+ const result = handleTool("xanoscript_docs", { mode: 123 });
78
+ expect(result.success).toBe(false);
79
+ expect(result.error).toContain("Invalid arguments");
80
+ });
81
+ it("should return validation error for invalid enum value", () => {
82
+ const result = handleTool("xanoscript_docs", { mode: "invalid_mode" });
83
+ expect(result.success).toBe(false);
84
+ expect(result.error).toContain("Invalid arguments");
85
+ });
86
+ it("should return validation error when meta_api_docs topic is wrong type", () => {
87
+ const result = handleTool("meta_api_docs", { topic: 123 });
88
+ expect(result.success).toBe(false);
89
+ expect(result.error).toContain("Invalid arguments");
90
+ });
91
+ });
92
+ describe("toMcpResponse", () => {
93
+ it("should convert success result to MCP response", () => {
94
+ const response = toMcpResponse({ success: true, data: "test data" });
95
+ expect(response.content).toHaveLength(1);
96
+ expect(response.content[0].type).toBe("text");
97
+ expect(response.content[0].text).toBe("test data");
98
+ expect(response.isError).toBeUndefined();
99
+ });
100
+ it("should convert error result to MCP response with isError", () => {
101
+ const response = toMcpResponse({ success: false, error: "test error" });
102
+ expect(response.content).toHaveLength(1);
103
+ expect(response.content[0].text).toBe("test error");
104
+ expect(response.isError).toBe(true);
105
+ });
106
+ it("should convert string array data to multiple content blocks", () => {
107
+ const response = toMcpResponse({
108
+ success: true,
109
+ data: ["block one", "block two", "block three"],
110
+ });
111
+ expect(response.content).toHaveLength(3);
112
+ expect(response.content[0]).toEqual({ type: "text", text: "block one" });
113
+ expect(response.content[1]).toEqual({ type: "text", text: "block two" });
114
+ expect(response.content[2]).toEqual({ type: "text", text: "block three" });
115
+ expect(response.isError).toBeUndefined();
116
+ });
117
+ it("should handle single-element array data", () => {
118
+ const response = toMcpResponse({ success: true, data: ["only block"] });
119
+ expect(response.content).toHaveLength(1);
120
+ expect(response.content[0].text).toBe("only block");
121
+ });
122
+ it("should include structuredContent when provided", () => {
123
+ const response = toMcpResponse({
124
+ success: true,
125
+ data: "test",
126
+ structuredContent: { version: "1.0.0" },
127
+ });
128
+ expect(response.structuredContent).toEqual({ version: "1.0.0" });
129
+ expect(response.isError).toBeUndefined();
130
+ });
131
+ it("should omit structuredContent when not provided", () => {
132
+ const response = toMcpResponse({ success: true, data: "test" });
133
+ expect(response.structuredContent).toBeUndefined();
134
+ });
135
+ it("should not include structuredContent on error", () => {
136
+ const response = toMcpResponse({ success: false, error: "fail" });
137
+ expect(response.structuredContent).toBeUndefined();
138
+ expect(response.isError).toBe(true);
139
+ });
140
+ });
141
+ describe("toolDefinitions", () => {
142
+ it("should contain all 5 tools", () => {
143
+ expect(toolDefinitions).toHaveLength(5);
144
+ });
145
+ it("should have unique tool names", () => {
146
+ const names = toolDefinitions.map((t) => t.name);
147
+ expect(new Set(names).size).toBe(names.length);
148
+ });
149
+ it("should include expected tool names", () => {
150
+ const names = toolDefinitions.map((t) => t.name);
151
+ expect(names).toContain("validate_xanoscript");
152
+ expect(names).toContain("xanoscript_docs");
153
+ expect(names).toContain("mcp_version");
154
+ expect(names).toContain("meta_api_docs");
155
+ expect(names).toContain("cli_docs");
156
+ });
157
+ it("should have annotations on all tools", () => {
158
+ for (const tool of toolDefinitions) {
159
+ expect(tool).toHaveProperty("annotations");
160
+ expect(tool.annotations).toHaveProperty("readOnlyHint");
161
+ expect(tool.annotations).toHaveProperty("destructiveHint", false);
162
+ }
163
+ });
164
+ it("should have outputSchema on all tools", () => {
165
+ for (const tool of toolDefinitions) {
166
+ expect(tool).toHaveProperty("outputSchema");
167
+ expect(tool.outputSchema).toHaveProperty("type", "object");
168
+ expect(tool.outputSchema).toHaveProperty("properties");
169
+ }
170
+ });
171
+ });
172
+ describe("integration: all topic files exist on disk", () => {
173
+ for (const [topic, config] of Object.entries(XANOSCRIPT_DOCS_V2)) {
174
+ it(`topic "${topic}" -> file "${config.file}" should exist`, () => {
175
+ const filePath = join(DOCS_PATH, config.file);
176
+ expect(existsSync(filePath)).toBe(true);
177
+ });
178
+ }
179
+ });
@@ -48,4 +48,14 @@ export declare const mcpVersionToolDefinition: {
48
48
  properties: {};
49
49
  required: never[];
50
50
  };
51
+ outputSchema: {
52
+ type: string;
53
+ properties: {
54
+ version: {
55
+ type: string;
56
+ description: string;
57
+ };
58
+ };
59
+ required: string[];
60
+ };
51
61
  };
@@ -74,9 +74,11 @@ export function mcpVersion() {
74
74
  * Get the MCP version and return a ToolResult.
75
75
  */
76
76
  export function mcpVersionTool() {
77
+ const version = getServerVersion();
77
78
  return {
78
79
  success: true,
79
- data: getServerVersion(),
80
+ data: version,
81
+ structuredContent: { version },
80
82
  };
81
83
  }
82
84
  // =============================================================================
@@ -97,4 +99,14 @@ export const mcpVersionToolDefinition = {
97
99
  properties: {},
98
100
  required: [],
99
101
  },
102
+ outputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ version: {
106
+ type: "string",
107
+ description: "The semantic version string of the MCP server.",
108
+ },
109
+ },
110
+ required: ["version"],
111
+ },
100
112
  };
@@ -55,6 +55,7 @@ export function metaApiDocsTool(args) {
55
55
  return {
56
56
  success: true,
57
57
  data: result.documentation,
58
+ structuredContent: { documentation: result.documentation },
58
59
  };
59
60
  }
60
61
  catch (error) {
@@ -1,18 +1,27 @@
1
1
  /**
2
2
  * Common types for tool results
3
3
  */
4
- export interface ToolResult {
5
- success: boolean;
6
- data?: string;
7
- error?: string;
8
- }
4
+ export type ToolResult = {
5
+ success: true;
6
+ data: string | string[];
7
+ structuredContent?: Record<string, unknown>;
8
+ error?: undefined;
9
+ } | {
10
+ success: false;
11
+ error: string;
12
+ data?: undefined;
13
+ structuredContent?: undefined;
14
+ };
9
15
  /**
10
- * Convert a ToolResult to MCP tool response format
16
+ * Convert a ToolResult to MCP tool response format.
17
+ * When data is a string[], each element becomes a separate content block.
18
+ * When structuredContent is provided, it's included for clients that support outputSchema.
11
19
  */
12
20
  export declare function toMcpResponse(result: ToolResult): {
13
21
  content: {
14
22
  type: "text";
15
23
  text: string;
16
24
  }[];
25
+ structuredContent?: Record<string, unknown>;
17
26
  isError?: boolean;
18
27
  };
@@ -2,13 +2,19 @@
2
2
  * Common types for tool results
3
3
  */
4
4
  /**
5
- * Convert a ToolResult to MCP tool response format
5
+ * Convert a ToolResult to MCP tool response format.
6
+ * When data is a string[], each element becomes a separate content block.
7
+ * When structuredContent is provided, it's included for clients that support outputSchema.
6
8
  */
7
9
  export function toMcpResponse(result) {
8
10
  if (result.success) {
9
- return {
10
- content: [{ type: "text", text: result.data }],
11
- };
11
+ const content = Array.isArray(result.data)
12
+ ? result.data.map((text) => ({ type: "text", text }))
13
+ : [{ type: "text", text: result.data }];
14
+ if (result.structuredContent) {
15
+ return { content, structuredContent: result.structuredContent };
16
+ }
17
+ return { content };
12
18
  }
13
19
  return {
14
20
  content: [{ type: "text", text: result.error }],
@@ -145,5 +145,19 @@ export declare const validateXanoscriptToolDefinition: {
145
145
  };
146
146
  required: never[];
147
147
  };
148
+ outputSchema: {
149
+ type: string;
150
+ properties: {
151
+ valid: {
152
+ type: string;
153
+ description: string;
154
+ };
155
+ message: {
156
+ type: string;
157
+ description: string;
158
+ };
159
+ };
160
+ required: string[];
161
+ };
148
162
  };
149
163
  export { TYPE_ALIASES, RESERVED_VARIABLES };
@@ -377,11 +377,14 @@ export function validateXanoscript(args) {
377
377
  */
378
378
  export function validateXanoscriptTool(args) {
379
379
  const result = validateXanoscript(args);
380
- return {
381
- success: result.valid,
382
- data: result.valid ? result.message : undefined,
383
- error: result.valid ? undefined : result.message,
384
- };
380
+ if (result.valid) {
381
+ return {
382
+ success: true,
383
+ data: result.message,
384
+ structuredContent: { valid: true, message: result.message },
385
+ };
386
+ }
387
+ return { success: false, error: result.message };
385
388
  }
386
389
  // =============================================================================
387
390
  // MCP Tool Definition
@@ -433,6 +436,20 @@ export const validateXanoscriptToolDefinition = {
433
436
  },
434
437
  required: [],
435
438
  },
439
+ outputSchema: {
440
+ type: "object",
441
+ properties: {
442
+ valid: {
443
+ type: "boolean",
444
+ description: "Whether the code passed validation without errors.",
445
+ },
446
+ message: {
447
+ type: "string",
448
+ description: "Human-readable validation summary with error details if any.",
449
+ },
450
+ },
451
+ required: ["valid", "message"],
452
+ },
436
453
  };
437
454
  // =============================================================================
438
455
  // Utility Exports
@@ -4,11 +4,13 @@
4
4
  * Retrieves XanoScript programming language documentation.
5
5
  * Can be used standalone or within the MCP server.
6
6
  */
7
- import { type XanoscriptDocsArgs } from "../xanoscript.js";
7
+ import { type XanoscriptDocsArgs, type TopicDoc } from "../xanoscript.js";
8
8
  import type { ToolResult } from "./types.js";
9
9
  export type { XanoscriptDocsArgs };
10
+ export type { TopicDoc };
10
11
  export interface XanoscriptDocsResult {
11
12
  documentation: string;
13
+ topics?: TopicDoc[];
12
14
  }
13
15
  /**
14
16
  * Get the path to XanoScript documentation files.
@@ -45,6 +47,7 @@ export declare function setXanoscriptDocsPath(path: string): void;
45
47
  export declare function xanoscriptDocs(args?: XanoscriptDocsArgs): XanoscriptDocsResult;
46
48
  /**
47
49
  * Get XanoScript documentation and return a ToolResult.
50
+ * For file_path mode, returns each topic as a separate content block.
48
51
  */
49
52
  export declare function xanoscriptDocsTool(args?: XanoscriptDocsArgs): ToolResult;
50
53
  export declare const xanoscriptDocsToolDefinition: {
@@ -83,4 +86,32 @@ export declare const xanoscriptDocsToolDefinition: {
83
86
  };
84
87
  required: never[];
85
88
  };
89
+ outputSchema: {
90
+ type: string;
91
+ properties: {
92
+ documentation: {
93
+ type: string;
94
+ description: string;
95
+ };
96
+ file_path: {
97
+ type: string;
98
+ description: string;
99
+ };
100
+ mode: {
101
+ type: string;
102
+ description: string;
103
+ };
104
+ version: {
105
+ type: string;
106
+ description: string;
107
+ };
108
+ topics: {
109
+ type: string;
110
+ items: {
111
+ type: string;
112
+ };
113
+ description: string;
114
+ };
115
+ };
116
+ };
86
117
  };
@@ -7,7 +7,7 @@
7
7
  import { readFileSync } from "fs";
8
8
  import { dirname, join } from "path";
9
9
  import { fileURLToPath } from "url";
10
- import { readXanoscriptDocsV2, getTopicNames, getTopicDescriptions, } from "../xanoscript.js";
10
+ import { readXanoscriptDocsV2, readXanoscriptDocsStructured, getXanoscriptDocsVersion, getTopicNames, getTopicDescriptions, } from "../xanoscript.js";
11
11
  // =============================================================================
12
12
  // Path Resolution
13
13
  // =============================================================================
@@ -75,17 +75,53 @@ export function setXanoscriptDocsPath(path) {
75
75
  export function xanoscriptDocs(args) {
76
76
  const docsPath = getXanoscriptDocsPath();
77
77
  const documentation = readXanoscriptDocsV2(docsPath, args);
78
+ // For file_path mode, also provide structured per-topic access
79
+ if (args?.file_path) {
80
+ const topics = readXanoscriptDocsStructured(docsPath, {
81
+ ...args,
82
+ file_path: args.file_path,
83
+ });
84
+ return { documentation, topics };
85
+ }
78
86
  return { documentation };
79
87
  }
80
88
  /**
81
89
  * Get XanoScript documentation and return a ToolResult.
90
+ * For file_path mode, returns each topic as a separate content block.
82
91
  */
83
92
  export function xanoscriptDocsTool(args) {
84
93
  try {
94
+ const docsPath = getXanoscriptDocsPath();
95
+ // file_path mode: return structured multi-content (one block per topic)
96
+ if (args?.file_path) {
97
+ const version = getXanoscriptDocsVersion(docsPath);
98
+ const topicDocs = readXanoscriptDocsStructured(docsPath, {
99
+ ...args,
100
+ file_path: args.file_path,
101
+ });
102
+ const mode = args.mode || "quick_reference";
103
+ const topics = topicDocs.map((d) => d.topic);
104
+ const header = `XanoScript Documentation for: ${args.file_path}\n` +
105
+ `Matched topics: ${topics.join(", ")}\n` +
106
+ `Mode: ${mode}\n` +
107
+ `Version: ${version}`;
108
+ return {
109
+ success: true,
110
+ data: [header, ...topicDocs.map((d) => d.content)],
111
+ structuredContent: {
112
+ file_path: args.file_path,
113
+ mode,
114
+ version,
115
+ topics,
116
+ },
117
+ };
118
+ }
119
+ // All other modes: return single content block
85
120
  const result = xanoscriptDocs(args);
86
121
  return {
87
122
  success: true,
88
123
  data: result.documentation,
124
+ structuredContent: { documentation: result.documentation },
89
125
  };
90
126
  }
91
127
  catch (error) {
@@ -104,7 +140,7 @@ export const xanoscriptDocsToolDefinition = {
104
140
  description: "Get XanoScript programming language documentation for AI code generation. " +
105
141
  "Call without parameters for overview (README). " +
106
142
  "Use 'topic' for specific documentation, or 'file_path' for context-aware docs based on the file you're editing. " +
107
- "Use mode='quick_reference' for compact syntax cheatsheet (recommended for context efficiency). " +
143
+ "Use mode='quick_reference' for compact syntax reference (recommended for context efficiency). " +
108
144
  "file_path mode defaults to 'quick_reference' to reduce context size; use mode='full' to get complete docs.",
109
145
  annotations: {
110
146
  readOnlyHint: true,
@@ -131,9 +167,11 @@ export const xanoscriptDocsToolDefinition = {
131
167
  },
132
168
  mode: {
133
169
  type: "string",
134
- enum: ["full", "quick_reference"],
170
+ enum: ["full", "quick_reference", "index"],
135
171
  description: "'full' = complete documentation with explanations and examples. " +
136
- "'quick_reference' = compact cheatsheet with just syntax patterns and signatures. " +
172
+ "'quick_reference' = compact reference with just syntax patterns and signatures. " +
173
+ "'index' = compact topic listing with descriptions and byte sizes (~1KB). " +
174
+ "Use 'index' to discover available topics before loading them. " +
137
175
  "Use 'quick_reference' to save context window space when you just need a reminder. " +
138
176
  "Default: 'full' for topic mode, 'quick_reference' for file_path mode.",
139
177
  },
@@ -141,10 +179,36 @@ export const xanoscriptDocsToolDefinition = {
141
179
  type: "array",
142
180
  items: { type: "string" },
143
181
  description: "List of topic names to exclude from file_path results. " +
144
- "Use this to skip topics you've already loaded (e.g., exclude_topics: ['syntax', 'quickstart']). " +
182
+ "Use this to skip topics you've already loaded (e.g., exclude_topics: ['syntax', 'essentials']). " +
145
183
  "Only applies when using file_path parameter.",
146
184
  },
147
185
  },
148
186
  required: [],
149
187
  },
188
+ outputSchema: {
189
+ type: "object",
190
+ properties: {
191
+ documentation: {
192
+ type: "string",
193
+ description: "The documentation content (topic or README mode).",
194
+ },
195
+ file_path: {
196
+ type: "string",
197
+ description: "The file path that was matched (file_path mode only).",
198
+ },
199
+ mode: {
200
+ type: "string",
201
+ description: "The documentation mode used.",
202
+ },
203
+ version: {
204
+ type: "string",
205
+ description: "The XanoScript documentation version.",
206
+ },
207
+ topics: {
208
+ type: "array",
209
+ items: { type: "string" },
210
+ description: "List of matched topic names (file_path mode only).",
211
+ },
212
+ },
213
+ },
150
214
  };
@@ -0,0 +1 @@
1
+ export {};