@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.
- package/README.md +42 -10
- package/dist/cli_docs/index.d.ts +10 -0
- package/dist/cli_docs/index.js +10 -0
- package/dist/index.js +2 -18
- package/dist/lib.d.ts +2 -2
- package/dist/lib.js +1 -1
- package/dist/meta_api_docs/index.d.ts +10 -0
- package/dist/meta_api_docs/index.js +10 -0
- package/dist/tools/cli_docs.js +1 -0
- package/dist/tools/index.d.ts +138 -2
- package/dist/tools/index.js +60 -8
- package/dist/tools/index.test.d.ts +1 -0
- package/dist/tools/index.test.js +179 -0
- package/dist/tools/mcp_version.d.ts +10 -0
- package/dist/tools/mcp_version.js +13 -1
- package/dist/tools/meta_api_docs.js +1 -0
- package/dist/tools/types.d.ts +15 -6
- package/dist/tools/types.js +10 -4
- package/dist/tools/validate_xanoscript.d.ts +14 -0
- package/dist/tools/validate_xanoscript.js +22 -5
- package/dist/tools/xanoscript_docs.d.ts +32 -1
- package/dist/tools/xanoscript_docs.js +69 -5
- package/dist/tools/xanoscript_docs.test.d.ts +1 -0
- package/dist/tools/xanoscript_docs.test.js +197 -0
- package/dist/xanoscript.d.ts +17 -1
- package/dist/xanoscript.js +99 -179
- package/dist/xanoscript.test.js +84 -8
- package/dist/xanoscript_docs/README.md +7 -7
- package/dist/xanoscript_docs/cheatsheet.md +4 -1
- package/dist/xanoscript_docs/database.md +2 -2
- package/dist/xanoscript_docs/docs_index.json +186 -108
- package/dist/xanoscript_docs/essentials.md +665 -0
- package/dist/xanoscript_docs/functions.md +1 -1
- package/dist/xanoscript_docs/middleware.md +5 -18
- package/dist/xanoscript_docs/quickstart.md +15 -6
- package/dist/xanoscript_docs/security.md +18 -43
- package/dist/xanoscript_docs/syntax/array-filters.md +238 -0
- package/dist/xanoscript_docs/syntax/functions.md +136 -0
- package/dist/xanoscript_docs/syntax/string-filters.md +188 -0
- package/dist/xanoscript_docs/syntax.md +92 -900
- package/dist/xanoscript_docs/triggers.md +1 -1
- package/dist/xanoscript_docs/types.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { getXanoscriptDocsPath, setXanoscriptDocsPath, xanoscriptDocs, xanoscriptDocsTool, } from "./xanoscript_docs.js";
|
|
5
|
+
import { toMcpResponse } from "./types.js";
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
const DOCS_PATH = join(__dirname, "..", "xanoscript_docs");
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
// Reset to default so tests don't interfere with each other
|
|
11
|
+
setXanoscriptDocsPath(DOCS_PATH);
|
|
12
|
+
});
|
|
13
|
+
describe("getXanoscriptDocsPath", () => {
|
|
14
|
+
it("should return a string path", () => {
|
|
15
|
+
const path = getXanoscriptDocsPath();
|
|
16
|
+
expect(typeof path).toBe("string");
|
|
17
|
+
expect(path.length).toBeGreaterThan(0);
|
|
18
|
+
});
|
|
19
|
+
it("should return a path ending with xanoscript_docs", () => {
|
|
20
|
+
const path = getXanoscriptDocsPath();
|
|
21
|
+
expect(path).toMatch(/xanoscript_docs$/);
|
|
22
|
+
});
|
|
23
|
+
it("should return a consistent path on repeated calls", () => {
|
|
24
|
+
const path1 = getXanoscriptDocsPath();
|
|
25
|
+
const path2 = getXanoscriptDocsPath();
|
|
26
|
+
expect(path1).toBe(path2);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("setXanoscriptDocsPath", () => {
|
|
30
|
+
it("should override the docs path", () => {
|
|
31
|
+
const customPath = "/custom/docs/path";
|
|
32
|
+
setXanoscriptDocsPath(customPath);
|
|
33
|
+
expect(getXanoscriptDocsPath()).toBe(customPath);
|
|
34
|
+
});
|
|
35
|
+
it("should be used by xanoscriptDocs when set", () => {
|
|
36
|
+
setXanoscriptDocsPath(DOCS_PATH);
|
|
37
|
+
const result = xanoscriptDocs();
|
|
38
|
+
expect(result.documentation).toContain("Documentation version:");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("xanoscriptDocs", () => {
|
|
42
|
+
it("should return README when called with no args", () => {
|
|
43
|
+
const result = xanoscriptDocs();
|
|
44
|
+
expect(result).toHaveProperty("documentation");
|
|
45
|
+
expect(typeof result.documentation).toBe("string");
|
|
46
|
+
expect(result.documentation).toContain("Documentation version:");
|
|
47
|
+
});
|
|
48
|
+
it("should return specific topic documentation", () => {
|
|
49
|
+
const result = xanoscriptDocs({ topic: "syntax" });
|
|
50
|
+
expect(result.documentation).toContain("Documentation version:");
|
|
51
|
+
});
|
|
52
|
+
it("should return context-aware docs for file_path", () => {
|
|
53
|
+
const result = xanoscriptDocs({ file_path: "apis/users/create.xs" });
|
|
54
|
+
expect(result.documentation).toContain("XanoScript Documentation for:");
|
|
55
|
+
expect(result.documentation).toContain("Matched topics:");
|
|
56
|
+
});
|
|
57
|
+
it("should support quick_reference mode", () => {
|
|
58
|
+
const full = xanoscriptDocs({ topic: "syntax", mode: "full" });
|
|
59
|
+
const quick = xanoscriptDocs({ topic: "syntax", mode: "quick_reference" });
|
|
60
|
+
expect(quick.documentation.length).toBeLessThanOrEqual(full.documentation.length);
|
|
61
|
+
});
|
|
62
|
+
it("should throw for unknown topic", () => {
|
|
63
|
+
expect(() => xanoscriptDocs({ topic: "nonexistent" })).toThrow('Unknown topic "nonexistent"');
|
|
64
|
+
});
|
|
65
|
+
it("should throw for invalid docs path", () => {
|
|
66
|
+
setXanoscriptDocsPath("/nonexistent/path");
|
|
67
|
+
expect(() => xanoscriptDocs({ topic: "syntax" })).toThrow();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("xanoscriptDocsTool", () => {
|
|
71
|
+
it("should return success with data for valid call", () => {
|
|
72
|
+
const result = xanoscriptDocsTool();
|
|
73
|
+
expect(result.success).toBe(true);
|
|
74
|
+
expect(result.data).toBeDefined();
|
|
75
|
+
expect(typeof result.data).toBe("string");
|
|
76
|
+
expect(result.error).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
it("should return success with topic docs", () => {
|
|
79
|
+
const result = xanoscriptDocsTool({ topic: "syntax" });
|
|
80
|
+
expect(result.success).toBe(true);
|
|
81
|
+
expect(result.data).toContain("Documentation version:");
|
|
82
|
+
});
|
|
83
|
+
it("should return error for unknown topic", () => {
|
|
84
|
+
const result = xanoscriptDocsTool({ topic: "nonexistent" });
|
|
85
|
+
expect(result.success).toBe(false);
|
|
86
|
+
expect(result.error).toBeDefined();
|
|
87
|
+
expect(result.error).toContain("Unknown topic");
|
|
88
|
+
});
|
|
89
|
+
it("should return error for invalid docs path", () => {
|
|
90
|
+
setXanoscriptDocsPath("/nonexistent/path");
|
|
91
|
+
const result = xanoscriptDocsTool({ topic: "syntax" });
|
|
92
|
+
expect(result.success).toBe(false);
|
|
93
|
+
expect(result.error).toBeDefined();
|
|
94
|
+
expect(result.error).toContain("Error retrieving XanoScript documentation");
|
|
95
|
+
});
|
|
96
|
+
it("should return multi-content array for file_path mode", () => {
|
|
97
|
+
const result = xanoscriptDocsTool({ file_path: "apis/users/create.xs" });
|
|
98
|
+
expect(result.success).toBe(true);
|
|
99
|
+
expect(Array.isArray(result.data)).toBe(true);
|
|
100
|
+
const data = result.data;
|
|
101
|
+
// First element is the header
|
|
102
|
+
expect(data[0]).toContain("XanoScript Documentation for: apis/users/create.xs");
|
|
103
|
+
expect(data[0]).toContain("Matched topics:");
|
|
104
|
+
expect(data[0]).toContain("Version:");
|
|
105
|
+
// Remaining elements are per-topic content
|
|
106
|
+
expect(data.length).toBeGreaterThan(1);
|
|
107
|
+
});
|
|
108
|
+
it("should return single string for topic mode", () => {
|
|
109
|
+
const result = xanoscriptDocsTool({ topic: "syntax" });
|
|
110
|
+
expect(result.success).toBe(true);
|
|
111
|
+
expect(typeof result.data).toBe("string");
|
|
112
|
+
expect(Array.isArray(result.data)).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
it("should return single string for no-args mode", () => {
|
|
115
|
+
const result = xanoscriptDocsTool();
|
|
116
|
+
expect(result.success).toBe(true);
|
|
117
|
+
expect(typeof result.data).toBe("string");
|
|
118
|
+
expect(Array.isArray(result.data)).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
it("should produce multiple MCP content blocks for file_path via toMcpResponse", () => {
|
|
121
|
+
const result = xanoscriptDocsTool({ file_path: "apis/users/create.xs" });
|
|
122
|
+
const mcpResponse = toMcpResponse(result);
|
|
123
|
+
expect(mcpResponse.isError).toBeUndefined();
|
|
124
|
+
// Should have multiple content blocks (header + N topics)
|
|
125
|
+
expect(mcpResponse.content.length).toBeGreaterThan(1);
|
|
126
|
+
// All blocks should be text type
|
|
127
|
+
for (const block of mcpResponse.content) {
|
|
128
|
+
expect(block.type).toBe("text");
|
|
129
|
+
expect(typeof block.text).toBe("string");
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it("should produce single MCP content block for topic mode via toMcpResponse", () => {
|
|
133
|
+
const result = xanoscriptDocsTool({ topic: "syntax" });
|
|
134
|
+
const mcpResponse = toMcpResponse(result);
|
|
135
|
+
expect(mcpResponse.content).toHaveLength(1);
|
|
136
|
+
expect(mcpResponse.content[0].type).toBe("text");
|
|
137
|
+
});
|
|
138
|
+
it("should include structuredContent for file_path mode", () => {
|
|
139
|
+
const result = xanoscriptDocsTool({ file_path: "apis/users/create.xs" });
|
|
140
|
+
expect(result.success).toBe(true);
|
|
141
|
+
expect(result.structuredContent).toBeDefined();
|
|
142
|
+
expect(result.structuredContent).toHaveProperty("file_path", "apis/users/create.xs");
|
|
143
|
+
expect(result.structuredContent).toHaveProperty("mode", "quick_reference");
|
|
144
|
+
expect(result.structuredContent).toHaveProperty("version");
|
|
145
|
+
expect(result.structuredContent).toHaveProperty("topics");
|
|
146
|
+
expect(Array.isArray(result.structuredContent.topics)).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
it("should include structuredContent for topic mode", () => {
|
|
149
|
+
const result = xanoscriptDocsTool({ topic: "syntax" });
|
|
150
|
+
expect(result.success).toBe(true);
|
|
151
|
+
expect(result.structuredContent).toBeDefined();
|
|
152
|
+
expect(result.structuredContent).toHaveProperty("documentation");
|
|
153
|
+
});
|
|
154
|
+
it("should include structuredContent in MCP response", () => {
|
|
155
|
+
const result = xanoscriptDocsTool({ file_path: "apis/users/create.xs" });
|
|
156
|
+
const mcpResponse = toMcpResponse(result);
|
|
157
|
+
expect(mcpResponse.structuredContent).toBeDefined();
|
|
158
|
+
expect(mcpResponse.structuredContent).toHaveProperty("file_path");
|
|
159
|
+
expect(mcpResponse.structuredContent).toHaveProperty("topics");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe("xanoscriptDocs structured output", () => {
|
|
163
|
+
it("should include topics array for file_path mode", () => {
|
|
164
|
+
const result = xanoscriptDocs({ file_path: "apis/users/create.xs" });
|
|
165
|
+
expect(result.topics).toBeDefined();
|
|
166
|
+
expect(Array.isArray(result.topics)).toBe(true);
|
|
167
|
+
expect(result.topics.length).toBeGreaterThan(0);
|
|
168
|
+
});
|
|
169
|
+
it("should have topic and content fields in each TopicDoc", () => {
|
|
170
|
+
const result = xanoscriptDocs({ file_path: "apis/users/create.xs" });
|
|
171
|
+
for (const doc of result.topics) {
|
|
172
|
+
expect(doc).toHaveProperty("topic");
|
|
173
|
+
expect(doc).toHaveProperty("content");
|
|
174
|
+
expect(typeof doc.topic).toBe("string");
|
|
175
|
+
expect(typeof doc.content).toBe("string");
|
|
176
|
+
expect(doc.content.length).toBeGreaterThan(0);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
it("should not include topics array for topic mode", () => {
|
|
180
|
+
const result = xanoscriptDocs({ topic: "syntax" });
|
|
181
|
+
expect(result.topics).toBeUndefined();
|
|
182
|
+
});
|
|
183
|
+
it("should not include topics array for no-args mode", () => {
|
|
184
|
+
const result = xanoscriptDocs();
|
|
185
|
+
expect(result.topics).toBeUndefined();
|
|
186
|
+
});
|
|
187
|
+
it("should respect exclude_topics in structured output", () => {
|
|
188
|
+
const result = xanoscriptDocs({
|
|
189
|
+
file_path: "apis/users/create.xs",
|
|
190
|
+
exclude_topics: ["syntax", "essentials"],
|
|
191
|
+
});
|
|
192
|
+
expect(result.topics).toBeDefined();
|
|
193
|
+
const topicNames = result.topics.map((d) => d.topic);
|
|
194
|
+
expect(topicNames).not.toContain("syntax");
|
|
195
|
+
expect(topicNames).not.toContain("essentials");
|
|
196
|
+
});
|
|
197
|
+
});
|
package/dist/xanoscript.d.ts
CHANGED
|
@@ -12,10 +12,15 @@ export interface DocConfig {
|
|
|
12
12
|
export interface XanoscriptDocsArgs {
|
|
13
13
|
topic?: string;
|
|
14
14
|
file_path?: string;
|
|
15
|
-
mode?: "full" | "quick_reference";
|
|
15
|
+
mode?: "full" | "quick_reference" | "index";
|
|
16
16
|
exclude_topics?: string[];
|
|
17
17
|
}
|
|
18
18
|
export declare const XANOSCRIPT_DOCS_V2: Record<string, DocConfig>;
|
|
19
|
+
/**
|
|
20
|
+
* Clear all cached documentation content and version data.
|
|
21
|
+
* Useful for testing or when docs files change at runtime.
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearDocsCache(): void;
|
|
19
24
|
/**
|
|
20
25
|
* Get list of topics that apply to a given file path based on applyTo patterns
|
|
21
26
|
*/
|
|
@@ -32,6 +37,17 @@ export declare function getXanoscriptDocsVersion(docsPath: string): string;
|
|
|
32
37
|
* Read XanoScript documentation with v2 structure
|
|
33
38
|
*/
|
|
34
39
|
export declare function readXanoscriptDocsV2(docsPath: string, args?: XanoscriptDocsArgs): string;
|
|
40
|
+
export interface TopicDoc {
|
|
41
|
+
topic: string;
|
|
42
|
+
content: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read documentation as structured per-topic entries for file_path mode.
|
|
46
|
+
* Returns each matched topic as a separate object for multi-content MCP responses.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readXanoscriptDocsStructured(docsPath: string, args: XanoscriptDocsArgs & {
|
|
49
|
+
file_path: string;
|
|
50
|
+
}): TopicDoc[];
|
|
35
51
|
/**
|
|
36
52
|
* Get available topic names
|
|
37
53
|
*/
|
package/dist/xanoscript.js
CHANGED
|
@@ -7,181 +7,43 @@
|
|
|
7
7
|
import { readFileSync } from "fs";
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
import { minimatch } from "minimatch";
|
|
10
|
+
import docsIndex from "./xanoscript_docs/docs_index.json" with { type: "json" };
|
|
10
11
|
// =============================================================================
|
|
11
|
-
// Documentation Configuration
|
|
12
|
+
// Documentation Configuration (loaded from docs_index.json)
|
|
12
13
|
// =============================================================================
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
applyTo: ["functions/**/*.xs"],
|
|
47
|
-
description: "Reusable function stacks with inputs and responses",
|
|
48
|
-
},
|
|
49
|
-
apis: {
|
|
50
|
-
file: "apis.md",
|
|
51
|
-
applyTo: ["apis/**/*.xs"],
|
|
52
|
-
description: "HTTP endpoint definitions with authentication and CRUD patterns",
|
|
53
|
-
},
|
|
54
|
-
tasks: {
|
|
55
|
-
file: "tasks.md",
|
|
56
|
-
applyTo: ["tasks/*.xs"],
|
|
57
|
-
description: "Scheduled and cron jobs",
|
|
58
|
-
},
|
|
59
|
-
triggers: {
|
|
60
|
-
file: "triggers.md",
|
|
61
|
-
applyTo: ["triggers/**/*.xs"],
|
|
62
|
-
description: "Event-driven handlers (table, realtime, workspace, agent, MCP)",
|
|
63
|
-
},
|
|
64
|
-
database: {
|
|
65
|
-
file: "database.md",
|
|
66
|
-
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs", "tools/**/*.xs"],
|
|
67
|
-
description: "All db.* operations: query, get, add, edit, patch, delete",
|
|
68
|
-
},
|
|
69
|
-
agents: {
|
|
70
|
-
file: "agents.md",
|
|
71
|
-
applyTo: ["agents/**/*.xs"],
|
|
72
|
-
description: "AI agent configuration with LLM providers and tools",
|
|
73
|
-
},
|
|
74
|
-
tools: {
|
|
75
|
-
file: "tools.md",
|
|
76
|
-
applyTo: ["tools/**/*.xs"],
|
|
77
|
-
description: "AI tools for agents and MCP servers",
|
|
78
|
-
},
|
|
79
|
-
"mcp-servers": {
|
|
80
|
-
file: "mcp-servers.md",
|
|
81
|
-
applyTo: ["mcp_servers/**/*.xs"],
|
|
82
|
-
description: "MCP server definitions exposing tools",
|
|
83
|
-
},
|
|
84
|
-
"unit-testing": {
|
|
85
|
-
file: "unit-testing.md",
|
|
86
|
-
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "middleware/**/*.xs"],
|
|
87
|
-
description: "Unit tests, mocks, and assertions within functions, APIs, and middleware",
|
|
88
|
-
},
|
|
89
|
-
"workflow-tests": {
|
|
90
|
-
file: "workflow-tests.md",
|
|
91
|
-
applyTo: ["workflow_test/**/*.xs"],
|
|
92
|
-
description: "End-to-end workflow tests with data source selection and tags",
|
|
93
|
-
},
|
|
94
|
-
integrations: {
|
|
95
|
-
file: "integrations.md",
|
|
96
|
-
applyTo: [],
|
|
97
|
-
description: "External service integrations index - see sub-topics for details",
|
|
98
|
-
},
|
|
99
|
-
"integrations/cloud-storage": {
|
|
100
|
-
file: "integrations/cloud-storage.md",
|
|
101
|
-
applyTo: [],
|
|
102
|
-
description: "AWS S3, Azure Blob, and GCP Storage operations",
|
|
103
|
-
},
|
|
104
|
-
"integrations/search": {
|
|
105
|
-
file: "integrations/search.md",
|
|
106
|
-
applyTo: [],
|
|
107
|
-
description: "Elasticsearch, OpenSearch, and Algolia search operations",
|
|
108
|
-
},
|
|
109
|
-
"integrations/redis": {
|
|
110
|
-
file: "integrations/redis.md",
|
|
111
|
-
applyTo: [],
|
|
112
|
-
description: "Redis caching, rate limiting, and queue operations",
|
|
113
|
-
},
|
|
114
|
-
"integrations/external-apis": {
|
|
115
|
-
file: "integrations/external-apis.md",
|
|
116
|
-
applyTo: [],
|
|
117
|
-
description: "HTTP requests with api.request patterns",
|
|
118
|
-
},
|
|
119
|
-
"integrations/utilities": {
|
|
120
|
-
file: "integrations/utilities.md",
|
|
121
|
-
applyTo: [],
|
|
122
|
-
description: "Local storage, email, zip, and Lambda utilities",
|
|
123
|
-
},
|
|
124
|
-
frontend: {
|
|
125
|
-
file: "frontend.md",
|
|
126
|
-
applyTo: ["static/**/*"],
|
|
127
|
-
description: "Static frontend development and deployment",
|
|
128
|
-
},
|
|
129
|
-
run: {
|
|
130
|
-
file: "run.md",
|
|
131
|
-
applyTo: ["run/**/*.xs"],
|
|
132
|
-
description: "Run job and service configurations for the Xano Job Runner",
|
|
133
|
-
},
|
|
134
|
-
addons: {
|
|
135
|
-
file: "addons.md",
|
|
136
|
-
applyTo: ["addons/*.xs"],
|
|
137
|
-
description: "Reusable subqueries for fetching related data",
|
|
138
|
-
},
|
|
139
|
-
debugging: {
|
|
140
|
-
file: "debugging.md",
|
|
141
|
-
applyTo: ["**/*.xs"],
|
|
142
|
-
description: "Logging, inspecting, and debugging XanoScript execution",
|
|
143
|
-
},
|
|
144
|
-
performance: {
|
|
145
|
-
file: "performance.md",
|
|
146
|
-
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
147
|
-
description: "Performance optimization best practices",
|
|
148
|
-
},
|
|
149
|
-
realtime: {
|
|
150
|
-
file: "realtime.md",
|
|
151
|
-
applyTo: ["triggers/**/*.xs"],
|
|
152
|
-
description: "Real-time channels and events for push updates",
|
|
153
|
-
},
|
|
154
|
-
schema: {
|
|
155
|
-
file: "schema.md",
|
|
156
|
-
applyTo: [],
|
|
157
|
-
description: "Runtime schema parsing and validation",
|
|
158
|
-
},
|
|
159
|
-
security: {
|
|
160
|
-
file: "security.md",
|
|
161
|
-
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
162
|
-
description: "Security best practices for authentication and authorization",
|
|
163
|
-
},
|
|
164
|
-
streaming: {
|
|
165
|
-
file: "streaming.md",
|
|
166
|
-
applyTo: [],
|
|
167
|
-
description: "Streaming data from files, requests, and responses",
|
|
168
|
-
},
|
|
169
|
-
middleware: {
|
|
170
|
-
file: "middleware.md",
|
|
171
|
-
applyTo: ["middleware/**/*.xs"],
|
|
172
|
-
description: "Request/response interceptors for functions, queries, tasks, and tools",
|
|
173
|
-
},
|
|
174
|
-
branch: {
|
|
175
|
-
file: "branch.md",
|
|
176
|
-
applyTo: ["branch.xs"],
|
|
177
|
-
description: "Branch-level settings: middleware, history retention, visual styling",
|
|
178
|
-
},
|
|
179
|
-
workspace: {
|
|
180
|
-
file: "workspace.md",
|
|
181
|
-
applyTo: ["workspace/**/*.xs"],
|
|
182
|
-
description: "Workspace-level settings: environment variables, preferences, realtime",
|
|
183
|
-
},
|
|
184
|
-
};
|
|
14
|
+
function buildDocsConfig() {
|
|
15
|
+
const config = {};
|
|
16
|
+
for (const [key, topic] of Object.entries(docsIndex.topics)) {
|
|
17
|
+
config[key] = {
|
|
18
|
+
file: topic.file,
|
|
19
|
+
applyTo: topic.applyTo,
|
|
20
|
+
description: topic.description,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return config;
|
|
24
|
+
}
|
|
25
|
+
export const XANOSCRIPT_DOCS_V2 = buildDocsConfig();
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Content Cache
|
|
28
|
+
// =============================================================================
|
|
29
|
+
const _fileCache = new Map();
|
|
30
|
+
let _versionCache = null;
|
|
31
|
+
/**
|
|
32
|
+
* Clear all cached documentation content and version data.
|
|
33
|
+
* Useful for testing or when docs files change at runtime.
|
|
34
|
+
*/
|
|
35
|
+
export function clearDocsCache() {
|
|
36
|
+
_fileCache.clear();
|
|
37
|
+
_versionCache = null;
|
|
38
|
+
}
|
|
39
|
+
function cachedReadFile(filePath) {
|
|
40
|
+
const cached = _fileCache.get(filePath);
|
|
41
|
+
if (cached !== undefined)
|
|
42
|
+
return cached;
|
|
43
|
+
const content = readFileSync(filePath, "utf-8");
|
|
44
|
+
_fileCache.set(filePath, content);
|
|
45
|
+
return content;
|
|
46
|
+
}
|
|
185
47
|
// =============================================================================
|
|
186
48
|
// Core Functions
|
|
187
49
|
// =============================================================================
|
|
@@ -229,9 +91,14 @@ export function extractQuickReference(content, topic) {
|
|
|
229
91
|
* Get the documentation version from the version.json file
|
|
230
92
|
*/
|
|
231
93
|
export function getXanoscriptDocsVersion(docsPath) {
|
|
94
|
+
if (_versionCache && _versionCache.path === docsPath) {
|
|
95
|
+
return _versionCache.version;
|
|
96
|
+
}
|
|
232
97
|
try {
|
|
233
|
-
const versionFile =
|
|
234
|
-
|
|
98
|
+
const versionFile = cachedReadFile(join(docsPath, "version.json"));
|
|
99
|
+
const version = JSON.parse(versionFile).version || "unknown";
|
|
100
|
+
_versionCache = { path: docsPath, version };
|
|
101
|
+
return version;
|
|
235
102
|
}
|
|
236
103
|
catch {
|
|
237
104
|
return "unknown";
|
|
@@ -241,13 +108,39 @@ export function getXanoscriptDocsVersion(docsPath) {
|
|
|
241
108
|
* Read XanoScript documentation with v2 structure
|
|
242
109
|
*/
|
|
243
110
|
export function readXanoscriptDocsV2(docsPath, args) {
|
|
111
|
+
const version = getXanoscriptDocsVersion(docsPath);
|
|
112
|
+
// Index mode: return compact topic listing with byte sizes
|
|
113
|
+
if (args?.mode === "index") {
|
|
114
|
+
const rows = Object.entries(XANOSCRIPT_DOCS_V2).map(([name, config]) => {
|
|
115
|
+
let size;
|
|
116
|
+
try {
|
|
117
|
+
size = cachedReadFile(join(docsPath, config.file)).length;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
size = 0;
|
|
121
|
+
}
|
|
122
|
+
const sizeKb = (size / 1024).toFixed(1);
|
|
123
|
+
return `| ${name} | ${config.description} | ${sizeKb} KB |`;
|
|
124
|
+
});
|
|
125
|
+
return [
|
|
126
|
+
`# XanoScript Documentation Index`,
|
|
127
|
+
``,
|
|
128
|
+
`Version: ${version}`,
|
|
129
|
+
`Topics: ${rows.length}`,
|
|
130
|
+
``,
|
|
131
|
+
`| Topic | Description | Size |`,
|
|
132
|
+
`|-------|-------------|------|`,
|
|
133
|
+
...rows,
|
|
134
|
+
``,
|
|
135
|
+
`Use topic='<name>' to load a specific topic. Use mode='quick_reference' for compact output.`,
|
|
136
|
+
].join("\n");
|
|
137
|
+
}
|
|
244
138
|
// Default to quick_reference for file_path mode (loads many topics),
|
|
245
139
|
// full for topic mode (loads single topic)
|
|
246
140
|
const mode = args?.mode || (args?.file_path ? "quick_reference" : "full");
|
|
247
|
-
const version = getXanoscriptDocsVersion(docsPath);
|
|
248
141
|
// Default: return README
|
|
249
142
|
if (!args?.topic && !args?.file_path) {
|
|
250
|
-
const readme =
|
|
143
|
+
const readme = cachedReadFile(join(docsPath, "README.md"));
|
|
251
144
|
return `${readme}\n\n---\nDocumentation version: ${version}`;
|
|
252
145
|
}
|
|
253
146
|
// Context-aware: return docs matching file pattern
|
|
@@ -262,7 +155,7 @@ export function readXanoscriptDocsV2(docsPath, args) {
|
|
|
262
155
|
}
|
|
263
156
|
const docs = topics.map((t) => {
|
|
264
157
|
const config = XANOSCRIPT_DOCS_V2[t];
|
|
265
|
-
const content =
|
|
158
|
+
const content = cachedReadFile(join(docsPath, config.file));
|
|
266
159
|
return mode === "quick_reference"
|
|
267
160
|
? extractQuickReference(content, t)
|
|
268
161
|
: content;
|
|
@@ -276,12 +169,39 @@ export function readXanoscriptDocsV2(docsPath, args) {
|
|
|
276
169
|
const availableTopics = Object.keys(XANOSCRIPT_DOCS_V2).join(", ");
|
|
277
170
|
throw new Error(`Unknown topic "${args.topic}".\n\nAvailable topics: ${availableTopics}`);
|
|
278
171
|
}
|
|
279
|
-
const content =
|
|
172
|
+
const content = cachedReadFile(join(docsPath, config.file));
|
|
280
173
|
const doc = mode === "quick_reference"
|
|
281
174
|
? extractQuickReference(content, args.topic)
|
|
282
175
|
: content;
|
|
283
176
|
return `${doc}\n\n---\nDocumentation version: ${version}`;
|
|
284
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Read documentation as structured per-topic entries for file_path mode.
|
|
180
|
+
* Returns each matched topic as a separate object for multi-content MCP responses.
|
|
181
|
+
*/
|
|
182
|
+
export function readXanoscriptDocsStructured(docsPath, args) {
|
|
183
|
+
const mode = args.mode || "quick_reference";
|
|
184
|
+
let topics = getDocsForFilePath(args.file_path);
|
|
185
|
+
if (args.exclude_topics && args.exclude_topics.length > 0) {
|
|
186
|
+
topics = topics.filter((t) => !args.exclude_topics.includes(t));
|
|
187
|
+
}
|
|
188
|
+
if (topics.length === 0) {
|
|
189
|
+
throw new Error(`No documentation found for file pattern: ${args.file_path}\n\nAvailable topics: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`);
|
|
190
|
+
}
|
|
191
|
+
return topics.map((t) => {
|
|
192
|
+
const config = XANOSCRIPT_DOCS_V2[t];
|
|
193
|
+
const content = cachedReadFile(join(docsPath, config.file));
|
|
194
|
+
return {
|
|
195
|
+
topic: t,
|
|
196
|
+
content: mode === "quick_reference"
|
|
197
|
+
? extractQuickReference(content, t)
|
|
198
|
+
: content,
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// =============================================================================
|
|
203
|
+
// Topic Metadata
|
|
204
|
+
// =============================================================================
|
|
285
205
|
/**
|
|
286
206
|
* Get available topic names
|
|
287
207
|
*/
|