@xano/developer-mcp 1.0.0 → 1.0.2
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 +96 -31
- package/dist/index.js +335 -222
- package/dist/templates/init-workspace.d.ts +10 -0
- package/dist/templates/init-workspace.js +292 -0
- package/dist/templates/xanoscript-index.d.ts +9 -0
- package/dist/templates/xanoscript-index.js +61 -0
- package/package.json +4 -2
- package/xanoscript_docs/README.md +107 -1
- package/xanoscript_docs/agents.md +329 -0
- package/xanoscript_docs/apis.md +343 -0
- package/xanoscript_docs/database.md +417 -0
- package/xanoscript_docs/ephemeral.md +333 -0
- package/xanoscript_docs/frontend.md +291 -0
- package/xanoscript_docs/functions.md +232 -2035
- package/xanoscript_docs/integrations.md +439 -0
- package/xanoscript_docs/mcp-servers.md +190 -0
- package/xanoscript_docs/plan.md +192 -0
- package/xanoscript_docs/syntax.md +314 -0
- package/xanoscript_docs/tables.md +270 -0
- package/xanoscript_docs/tasks.md +254 -0
- package/xanoscript_docs/testing.md +335 -0
- package/xanoscript_docs/tools.md +305 -0
- package/xanoscript_docs/types.md +297 -0
- package/xanoscript_docs/version.json +2 -1
- package/xanoscript_docs/api_query_examples.md +0 -1255
- package/xanoscript_docs/api_query_guideline.md +0 -129
- package/xanoscript_docs/build_from_lovable.md +0 -715
- package/xanoscript_docs/db_query_guideline.md +0 -427
- package/xanoscript_docs/ephemeral_environment_guideline.md +0 -529
- package/xanoscript_docs/expression_guideline.md +0 -1086
- package/xanoscript_docs/frontend_guideline.md +0 -67
- package/xanoscript_docs/function_examples.md +0 -1406
- package/xanoscript_docs/function_guideline.md +0 -130
- package/xanoscript_docs/input_guideline.md +0 -227
- package/xanoscript_docs/mcp_server_examples.md +0 -36
- package/xanoscript_docs/mcp_server_guideline.md +0 -69
- package/xanoscript_docs/query_filter.md +0 -489
- package/xanoscript_docs/table_examples.md +0 -586
- package/xanoscript_docs/table_guideline.md +0 -137
- package/xanoscript_docs/task_examples.md +0 -511
- package/xanoscript_docs/task_guideline.md +0 -103
- package/xanoscript_docs/tips_and_tricks.md +0 -144
- package/xanoscript_docs/tool_examples.md +0 -69
- package/xanoscript_docs/tool_guideline.md +0 -139
- package/xanoscript_docs/unit_testing_guideline.md +0 -328
- package/xanoscript_docs/workspace.md +0 -17
package/dist/index.js
CHANGED
|
@@ -1,108 +1,159 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { readFileSync } from "fs";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { dirname, join } from "path";
|
|
8
|
+
import { minimatch } from "minimatch";
|
|
8
9
|
import { xanoscriptParser } from "@xano/xanoscript-language-server/parser/parser.js";
|
|
9
10
|
import { getSchemeFromContent } from "@xano/xanoscript-language-server/utils.js";
|
|
11
|
+
import { generateInitWorkspaceTemplate } from "./templates/init-workspace.js";
|
|
10
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
13
|
const __dirname = dirname(__filename);
|
|
12
|
-
|
|
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
|
-
|
|
14
|
+
const XANOSCRIPT_DOCS_V2 = {
|
|
15
|
+
readme: {
|
|
16
|
+
file: "README.md",
|
|
17
|
+
applyTo: [],
|
|
18
|
+
description: "XanoScript overview, workspace structure, and quick reference",
|
|
19
|
+
},
|
|
20
|
+
syntax: {
|
|
21
|
+
file: "syntax.md",
|
|
22
|
+
applyTo: ["**/*.xs"],
|
|
23
|
+
description: "Expressions, operators, and filters for all XanoScript code",
|
|
24
|
+
},
|
|
25
|
+
types: {
|
|
26
|
+
file: "types.md",
|
|
27
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tools/**/*.xs", "agents/**/*.xs"],
|
|
28
|
+
description: "Data types, input blocks, and validation",
|
|
29
|
+
},
|
|
30
|
+
tables: {
|
|
31
|
+
file: "tables.md",
|
|
32
|
+
applyTo: ["tables/*.xs"],
|
|
33
|
+
description: "Database schema definitions with indexes and relationships",
|
|
34
|
+
},
|
|
35
|
+
functions: {
|
|
36
|
+
file: "functions.md",
|
|
37
|
+
applyTo: ["functions/**/*.xs"],
|
|
38
|
+
description: "Reusable function stacks with inputs and responses",
|
|
39
|
+
},
|
|
40
|
+
apis: {
|
|
41
|
+
file: "apis.md",
|
|
42
|
+
applyTo: ["apis/**/*.xs"],
|
|
43
|
+
description: "HTTP endpoint definitions with authentication and CRUD patterns",
|
|
44
|
+
},
|
|
45
|
+
tasks: {
|
|
46
|
+
file: "tasks.md",
|
|
47
|
+
applyTo: ["tasks/*.xs"],
|
|
48
|
+
description: "Scheduled and cron jobs",
|
|
49
|
+
},
|
|
50
|
+
database: {
|
|
51
|
+
file: "database.md",
|
|
52
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs", "tools/**/*.xs"],
|
|
53
|
+
description: "All db.* operations: query, get, add, edit, patch, delete",
|
|
54
|
+
},
|
|
55
|
+
agents: {
|
|
56
|
+
file: "agents.md",
|
|
57
|
+
applyTo: ["agents/**/*.xs"],
|
|
58
|
+
description: "AI agent configuration with LLM providers and tools",
|
|
59
|
+
},
|
|
60
|
+
tools: {
|
|
61
|
+
file: "tools.md",
|
|
62
|
+
applyTo: ["tools/**/*.xs"],
|
|
63
|
+
description: "AI tools for agents and MCP servers",
|
|
64
|
+
},
|
|
65
|
+
"mcp-servers": {
|
|
66
|
+
file: "mcp-servers.md",
|
|
67
|
+
applyTo: ["mcp_servers/**/*.xs"],
|
|
68
|
+
description: "MCP server definitions exposing tools",
|
|
69
|
+
},
|
|
70
|
+
testing: {
|
|
71
|
+
file: "testing.md",
|
|
72
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
73
|
+
description: "Unit tests, mocks, and assertions",
|
|
74
|
+
},
|
|
75
|
+
integrations: {
|
|
76
|
+
file: "integrations.md",
|
|
77
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs"],
|
|
78
|
+
description: "Cloud storage, Redis, security, and external APIs",
|
|
79
|
+
},
|
|
80
|
+
frontend: {
|
|
81
|
+
file: "frontend.md",
|
|
82
|
+
applyTo: ["static/**/*"],
|
|
83
|
+
description: "Static frontend development and deployment",
|
|
84
|
+
},
|
|
85
|
+
ephemeral: {
|
|
86
|
+
file: "ephemeral.md",
|
|
87
|
+
applyTo: ["ephemeral/**/*.xs"],
|
|
88
|
+
description: "Temporary test environments",
|
|
89
|
+
},
|
|
42
90
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
91
|
+
const XANO_OBJECT_TYPES = {
|
|
92
|
+
function: {
|
|
93
|
+
path: "functions",
|
|
94
|
+
endpoint: "function",
|
|
95
|
+
extension: ".xs",
|
|
96
|
+
hasXanoscript: true,
|
|
97
|
+
},
|
|
98
|
+
table: {
|
|
99
|
+
path: "tables",
|
|
100
|
+
endpoint: "table",
|
|
101
|
+
extension: ".xs",
|
|
102
|
+
hasXanoscript: true,
|
|
103
|
+
},
|
|
104
|
+
task: {
|
|
105
|
+
path: "tasks",
|
|
106
|
+
endpoint: "task",
|
|
107
|
+
extension: ".xs",
|
|
108
|
+
hasXanoscript: true,
|
|
109
|
+
},
|
|
110
|
+
api_group: {
|
|
111
|
+
path: "apis",
|
|
112
|
+
endpoint: "api-group",
|
|
113
|
+
extension: ".xs",
|
|
114
|
+
hasXanoscript: true,
|
|
115
|
+
supportsNesting: true,
|
|
116
|
+
},
|
|
117
|
+
tool: {
|
|
118
|
+
path: "tools",
|
|
119
|
+
endpoint: "tool",
|
|
120
|
+
extension: ".xs",
|
|
121
|
+
hasXanoscript: true,
|
|
122
|
+
},
|
|
123
|
+
agent: {
|
|
124
|
+
path: "agents",
|
|
125
|
+
endpoint: "agent",
|
|
126
|
+
extension: ".xs",
|
|
127
|
+
hasXanoscript: true,
|
|
128
|
+
},
|
|
129
|
+
middleware: {
|
|
130
|
+
path: "middlewares",
|
|
131
|
+
endpoint: "middleware",
|
|
132
|
+
extension: ".xs",
|
|
133
|
+
hasXanoscript: true,
|
|
134
|
+
},
|
|
135
|
+
addon: {
|
|
136
|
+
path: "addons",
|
|
137
|
+
endpoint: "addon",
|
|
138
|
+
extension: ".xs",
|
|
139
|
+
hasXanoscript: true,
|
|
140
|
+
},
|
|
141
|
+
mcp_server: {
|
|
142
|
+
path: "mcp_servers",
|
|
143
|
+
endpoint: "mcp-server",
|
|
144
|
+
extension: ".xs",
|
|
145
|
+
hasXanoscript: true,
|
|
146
|
+
},
|
|
147
|
+
realtime_channel: {
|
|
148
|
+
path: "realtime",
|
|
149
|
+
endpoint: "realtime-channel",
|
|
150
|
+
extension: ".xs",
|
|
151
|
+
hasXanoscript: true,
|
|
152
|
+
},
|
|
104
153
|
};
|
|
105
|
-
//
|
|
154
|
+
// =============================================================================
|
|
155
|
+
// API Documentation Configuration
|
|
156
|
+
// =============================================================================
|
|
106
157
|
const DOCS_MAP = {
|
|
107
158
|
workspace: "workspace.md",
|
|
108
159
|
table: "table.md",
|
|
@@ -120,10 +171,10 @@ const DOCS_MAP = {
|
|
|
120
171
|
history: "history.md",
|
|
121
172
|
authentication: "authentication.md",
|
|
122
173
|
};
|
|
123
|
-
//
|
|
174
|
+
// =============================================================================
|
|
175
|
+
// Path Resolution
|
|
176
|
+
// =============================================================================
|
|
124
177
|
function getDocsPath() {
|
|
125
|
-
// In development, look relative to src
|
|
126
|
-
// In production (after build), look relative to dist
|
|
127
178
|
const possiblePaths = [
|
|
128
179
|
join(__dirname, "..", "api_docs"),
|
|
129
180
|
join(__dirname, "..", "..", "api_docs"),
|
|
@@ -139,8 +190,6 @@ function getDocsPath() {
|
|
|
139
190
|
}
|
|
140
191
|
return join(__dirname, "..", "api_docs");
|
|
141
192
|
}
|
|
142
|
-
const DOCS_PATH = getDocsPath();
|
|
143
|
-
// Get the xanoscript_docs directory path
|
|
144
193
|
function getXanoscriptDocsPath() {
|
|
145
194
|
const possiblePaths = [
|
|
146
195
|
join(__dirname, "..", "xanoscript_docs"),
|
|
@@ -157,26 +206,35 @@ function getXanoscriptDocsPath() {
|
|
|
157
206
|
}
|
|
158
207
|
return join(__dirname, "..", "xanoscript_docs");
|
|
159
208
|
}
|
|
209
|
+
const DOCS_PATH = getDocsPath();
|
|
160
210
|
const XANOSCRIPT_DOCS_PATH = getXanoscriptDocsPath();
|
|
211
|
+
// =============================================================================
|
|
212
|
+
// Documentation Helpers
|
|
213
|
+
// =============================================================================
|
|
214
|
+
function getXanoscriptDocsVersion() {
|
|
215
|
+
try {
|
|
216
|
+
const versionFile = readFileSync(join(XANOSCRIPT_DOCS_PATH, "version.json"), "utf-8");
|
|
217
|
+
return JSON.parse(versionFile).version || "unknown";
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return "unknown";
|
|
221
|
+
}
|
|
222
|
+
}
|
|
161
223
|
function readDocumentation(object) {
|
|
162
224
|
try {
|
|
163
225
|
if (!object) {
|
|
164
|
-
// Return index documentation
|
|
165
226
|
return readFileSync(join(DOCS_PATH, "index.md"), "utf-8");
|
|
166
227
|
}
|
|
167
228
|
const normalizedObject = object.toLowerCase().trim();
|
|
168
|
-
// Check if the object exists in our map
|
|
169
229
|
if (normalizedObject in DOCS_MAP) {
|
|
170
230
|
const filePath = join(DOCS_PATH, DOCS_MAP[normalizedObject]);
|
|
171
231
|
return readFileSync(filePath, "utf-8");
|
|
172
232
|
}
|
|
173
|
-
// Try to find a partial match
|
|
174
233
|
const matchingKey = Object.keys(DOCS_MAP).find((key) => key.includes(normalizedObject) || normalizedObject.includes(key));
|
|
175
234
|
if (matchingKey) {
|
|
176
235
|
const filePath = join(DOCS_PATH, DOCS_MAP[matchingKey]);
|
|
177
236
|
return readFileSync(filePath, "utf-8");
|
|
178
237
|
}
|
|
179
|
-
// Return error message with available options
|
|
180
238
|
const availableObjects = Object.keys(DOCS_MAP).join(", ");
|
|
181
239
|
return `Error: Unknown object "${object}". Available objects: ${availableObjects}
|
|
182
240
|
|
|
@@ -187,135 +245,159 @@ Use api_docs() without parameters to see the full documentation index.`;
|
|
|
187
245
|
return `Error reading documentation: ${errorMessage}`;
|
|
188
246
|
}
|
|
189
247
|
}
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
248
|
+
// =============================================================================
|
|
249
|
+
// XanoScript Documentation v2 Functions
|
|
250
|
+
// =============================================================================
|
|
251
|
+
/**
|
|
252
|
+
* Get list of topics that apply to a given file path based on applyTo patterns
|
|
253
|
+
*/
|
|
254
|
+
function getDocsForFilePath(filePath) {
|
|
255
|
+
const matches = [];
|
|
256
|
+
for (const [topic, config] of Object.entries(XANOSCRIPT_DOCS_V2)) {
|
|
257
|
+
if (topic === "readme")
|
|
258
|
+
continue; // Don't auto-include readme
|
|
259
|
+
for (const pattern of config.applyTo) {
|
|
260
|
+
if (minimatch(filePath, pattern)) {
|
|
261
|
+
matches.push(topic);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
195
265
|
}
|
|
196
|
-
|
|
197
|
-
|
|
266
|
+
// Always include syntax as foundation (if not already matched)
|
|
267
|
+
if (!matches.includes("syntax")) {
|
|
268
|
+
matches.unshift("syntax");
|
|
198
269
|
}
|
|
270
|
+
return matches;
|
|
199
271
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Extract just the Quick Reference section from a doc
|
|
274
|
+
*/
|
|
275
|
+
function extractQuickReference(content, topic) {
|
|
276
|
+
const lines = content.split("\n");
|
|
277
|
+
const startIdx = lines.findIndex((l) => l.startsWith("## Quick Reference"));
|
|
278
|
+
if (startIdx === -1) {
|
|
279
|
+
// Fallback: return first 50 lines or up to first ## section
|
|
280
|
+
const firstSection = lines.findIndex((l, i) => i > 0 && l.startsWith("## "));
|
|
281
|
+
return lines.slice(0, firstSection > 0 ? firstSection : 50).join("\n");
|
|
208
282
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
Use \`xanoscript_docs\` with a keyword to retrieve documentation.
|
|
217
|
-
|
|
218
|
-
## Core Concepts
|
|
219
|
-
These return guidelines + examples for writing XanoScript code.
|
|
220
|
-
|
|
221
|
-
| Keyword | Aliases | Description |
|
|
222
|
-
|---------|---------|-------------|
|
|
223
|
-
${formatRow("function", "Custom reusable functions in `functions/`")}
|
|
224
|
-
${formatRow("api_query", "HTTP API endpoints in `apis/`")}
|
|
225
|
-
${formatRow("table", "Database table schemas in `tables/`")}
|
|
226
|
-
${formatRow("task", "Scheduled background tasks in `tasks/`")}
|
|
227
|
-
${formatRow("tool", "AI-callable tools in `tools/`")}
|
|
228
|
-
${formatRow("agent", "AI agents in `agents/`")}
|
|
229
|
-
${formatRow("mcp_server", "MCP servers in `mcp_servers/`")}
|
|
230
|
-
|
|
231
|
-
## Language Reference
|
|
232
|
-
Core syntax and operators.
|
|
233
|
-
|
|
234
|
-
| Keyword | Aliases | Description |
|
|
235
|
-
|---------|---------|-------------|
|
|
236
|
-
${formatRow("syntax", "Complete XanoScript syntax (stack, var, conditional, foreach, etc.)")}
|
|
237
|
-
${formatRow("expressions", "Pipe operators and filters (string, math, array, date)")}
|
|
238
|
-
${formatRow("input", "Input definition syntax (types, filters, validation)")}
|
|
239
|
-
${formatRow("db_query", "Database query patterns (query, add, edit, delete)")}
|
|
240
|
-
${formatRow("query_filter", "WHERE clause and filter syntax")}
|
|
241
|
-
|
|
242
|
-
## Development Workflows
|
|
243
|
-
AI agent development strategies and phases.
|
|
244
|
-
|
|
245
|
-
| Keyword | Aliases | Description |
|
|
246
|
-
|---------|---------|-------------|
|
|
247
|
-
${formatRow("workflow", "Overall XanoScript development workflow")}
|
|
248
|
-
${formatRow("function_workflow", "AI workflow for creating functions")}
|
|
249
|
-
${formatRow("api_workflow", "AI workflow for creating API endpoints")}
|
|
250
|
-
${formatRow("table_workflow", "AI workflow for creating tables")}
|
|
251
|
-
${formatRow("task_workflow", "AI workflow for creating tasks")}
|
|
252
|
-
|
|
253
|
-
## Specialized Topics
|
|
254
|
-
|
|
255
|
-
| Keyword | Aliases | Description |
|
|
256
|
-
|---------|---------|-------------|
|
|
257
|
-
${formatRow("frontend", "Frontend development with Xano")}
|
|
258
|
-
${formatRow("lovable", "Building from Lovable-generated websites")}
|
|
259
|
-
${formatRow("testing", "Unit testing XanoScript code")}
|
|
260
|
-
${formatRow("tips", "Tips and tricks")}
|
|
261
|
-
${formatRow("ephemeral", "Ephemeral environment setup")}
|
|
262
|
-
`;
|
|
283
|
+
// Find the next ## section after Quick Reference
|
|
284
|
+
let endIdx = lines.findIndex((l, i) => i > startIdx && l.startsWith("## "));
|
|
285
|
+
if (endIdx === -1)
|
|
286
|
+
endIdx = lines.length;
|
|
287
|
+
// Include topic header for context
|
|
288
|
+
const header = `# ${topic}\n\n`;
|
|
289
|
+
return header + lines.slice(startIdx, endIdx).join("\n");
|
|
263
290
|
}
|
|
264
|
-
|
|
265
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Read XanoScript documentation with new v2 structure
|
|
293
|
+
*/
|
|
294
|
+
function readXanoscriptDocsV2(args) {
|
|
295
|
+
const mode = args?.mode || "full";
|
|
296
|
+
const version = getXanoscriptDocsVersion();
|
|
266
297
|
try {
|
|
267
|
-
|
|
268
|
-
|
|
298
|
+
// Default: return README
|
|
299
|
+
if (!args?.topic && !args?.file_path) {
|
|
300
|
+
const readme = readFileSync(join(XANOSCRIPT_DOCS_PATH, "README.md"), "utf-8");
|
|
301
|
+
return `${readme}\n\n---\nDocumentation version: ${version}`;
|
|
269
302
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Try partial match
|
|
276
|
-
const matchingKey = Object.keys(XANOSCRIPT_DOCS).find((key) => key.includes(resolvedKeyword) || resolvedKeyword.includes(key));
|
|
277
|
-
if (matchingKey) {
|
|
278
|
-
return readXanoscriptDocs(matchingKey);
|
|
303
|
+
// Context-aware: return docs matching file pattern
|
|
304
|
+
if (args?.file_path) {
|
|
305
|
+
const topics = getDocsForFilePath(args.file_path);
|
|
306
|
+
if (topics.length === 0) {
|
|
307
|
+
return `No documentation found for file pattern: ${args.file_path}\n\nAvailable topics: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`;
|
|
279
308
|
}
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
309
|
+
const docs = topics.map((t) => {
|
|
310
|
+
const config = XANOSCRIPT_DOCS_V2[t];
|
|
311
|
+
const content = readFileSync(join(XANOSCRIPT_DOCS_PATH, config.file), "utf-8");
|
|
312
|
+
return mode === "quick_reference"
|
|
313
|
+
? extractQuickReference(content, t)
|
|
314
|
+
: content;
|
|
315
|
+
});
|
|
316
|
+
const header = `# XanoScript Documentation for: ${args.file_path}\n\nMatched topics: ${topics.join(", ")}\nMode: ${mode}\nVersion: ${version}\n\n---\n\n`;
|
|
317
|
+
return header + docs.join("\n\n---\n\n");
|
|
284
318
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
for (const file of files) {
|
|
292
|
-
const filePath = join(XANOSCRIPT_DOCS_PATH, file);
|
|
293
|
-
try {
|
|
294
|
-
const content = readFileSync(filePath, "utf-8");
|
|
295
|
-
contents.push(`---\n## Source: ${file}\n---\n`);
|
|
296
|
-
contents.push(content);
|
|
297
|
-
}
|
|
298
|
-
catch (err) {
|
|
299
|
-
contents.push(`\n[Error reading ${file}: file not found]\n`);
|
|
319
|
+
// Topic-based: return specific doc
|
|
320
|
+
if (args?.topic) {
|
|
321
|
+
const config = XANOSCRIPT_DOCS_V2[args.topic];
|
|
322
|
+
if (!config) {
|
|
323
|
+
const availableTopics = Object.keys(XANOSCRIPT_DOCS_V2).join(", ");
|
|
324
|
+
return `Error: Unknown topic "${args.topic}".\n\nAvailable topics: ${availableTopics}`;
|
|
300
325
|
}
|
|
326
|
+
const content = readFileSync(join(XANOSCRIPT_DOCS_PATH, config.file), "utf-8");
|
|
327
|
+
const doc = mode === "quick_reference"
|
|
328
|
+
? extractQuickReference(content, args.topic)
|
|
329
|
+
: content;
|
|
330
|
+
return `${doc}\n\n---\nDocumentation version: ${version}`;
|
|
301
331
|
}
|
|
302
|
-
return
|
|
332
|
+
return "Error: Invalid parameters";
|
|
303
333
|
}
|
|
304
334
|
catch (error) {
|
|
305
335
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
306
336
|
return `Error reading XanoScript documentation: ${errorMessage}`;
|
|
307
337
|
}
|
|
308
338
|
}
|
|
309
|
-
//
|
|
339
|
+
// =============================================================================
|
|
340
|
+
// Init Workspace Documentation
|
|
341
|
+
// =============================================================================
|
|
342
|
+
function generateInitWorkspaceDoc() {
|
|
343
|
+
const objectTypes = Object.entries(XANO_OBJECT_TYPES).map(([type, config]) => ({
|
|
344
|
+
type,
|
|
345
|
+
path: config.path,
|
|
346
|
+
endpoint: config.endpoint,
|
|
347
|
+
}));
|
|
348
|
+
return generateInitWorkspaceTemplate(objectTypes);
|
|
349
|
+
}
|
|
350
|
+
// =============================================================================
|
|
351
|
+
// MCP Server Setup
|
|
352
|
+
// =============================================================================
|
|
310
353
|
const server = new Server({
|
|
311
354
|
name: "xano-developer-mcp",
|
|
312
355
|
version: "1.0.0",
|
|
356
|
+
description: "MCP server for Xano Headless API documentation and XanoScript code validation",
|
|
313
357
|
}, {
|
|
314
358
|
capabilities: {
|
|
315
359
|
tools: {},
|
|
360
|
+
resources: {},
|
|
316
361
|
},
|
|
317
362
|
});
|
|
318
|
-
//
|
|
363
|
+
// =============================================================================
|
|
364
|
+
// Resource Handlers (MCP Resources)
|
|
365
|
+
// =============================================================================
|
|
366
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
367
|
+
const resources = Object.entries(XANOSCRIPT_DOCS_V2).map(([key, config]) => ({
|
|
368
|
+
uri: `xanoscript://docs/${key}`,
|
|
369
|
+
name: key,
|
|
370
|
+
description: config.description,
|
|
371
|
+
mimeType: "text/markdown",
|
|
372
|
+
}));
|
|
373
|
+
return { resources };
|
|
374
|
+
});
|
|
375
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
376
|
+
const { uri } = request.params;
|
|
377
|
+
const match = uri.match(/^xanoscript:\/\/docs\/(.+)$/);
|
|
378
|
+
if (!match) {
|
|
379
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
380
|
+
}
|
|
381
|
+
const topic = match[1];
|
|
382
|
+
const config = XANOSCRIPT_DOCS_V2[topic];
|
|
383
|
+
if (!config) {
|
|
384
|
+
throw new Error(`Unknown topic: ${topic}. Available: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`);
|
|
385
|
+
}
|
|
386
|
+
const content = readFileSync(join(XANOSCRIPT_DOCS_PATH, config.file), "utf-8");
|
|
387
|
+
const version = getXanoscriptDocsVersion();
|
|
388
|
+
return {
|
|
389
|
+
contents: [
|
|
390
|
+
{
|
|
391
|
+
uri,
|
|
392
|
+
mimeType: "text/markdown",
|
|
393
|
+
text: `${content}\n\n---\nDocumentation version: ${version}`,
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
};
|
|
397
|
+
});
|
|
398
|
+
// =============================================================================
|
|
399
|
+
// Tool Handlers
|
|
400
|
+
// =============================================================================
|
|
319
401
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
320
402
|
return {
|
|
321
403
|
tools: [
|
|
@@ -350,27 +432,49 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
350
432
|
{
|
|
351
433
|
name: "xanoscript_docs",
|
|
352
434
|
description: "Get XanoScript programming language documentation for AI code generation. " +
|
|
353
|
-
"Call without
|
|
354
|
-
"Use
|
|
435
|
+
"Call without parameters for overview (README). " +
|
|
436
|
+
"Use 'topic' for specific documentation, or 'file_path' for context-aware docs based on the file you're editing. " +
|
|
437
|
+
"Use mode='quick_reference' for compact syntax cheatsheet (recommended for context efficiency).",
|
|
355
438
|
inputSchema: {
|
|
356
439
|
type: "object",
|
|
357
440
|
properties: {
|
|
358
|
-
|
|
441
|
+
topic: {
|
|
359
442
|
type: "string",
|
|
360
|
-
description: "Documentation topic
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
443
|
+
description: "Documentation topic. Available: " +
|
|
444
|
+
Object.entries(XANOSCRIPT_DOCS_V2)
|
|
445
|
+
.map(([k, v]) => `${k} (${v.description.split(".")[0]})`)
|
|
446
|
+
.join(", "),
|
|
447
|
+
},
|
|
448
|
+
file_path: {
|
|
449
|
+
type: "string",
|
|
450
|
+
description: "File path being edited (e.g., 'apis/users/create.xs', 'functions/utils/format.xs'). " +
|
|
451
|
+
"Returns all relevant docs based on file type using applyTo pattern matching.",
|
|
452
|
+
},
|
|
453
|
+
mode: {
|
|
454
|
+
type: "string",
|
|
455
|
+
enum: ["full", "quick_reference"],
|
|
456
|
+
description: "full = complete documentation, quick_reference = compact Quick Reference sections only. " +
|
|
457
|
+
"Use quick_reference for smaller context window usage.",
|
|
365
458
|
},
|
|
366
459
|
},
|
|
367
460
|
required: [],
|
|
368
461
|
},
|
|
369
462
|
},
|
|
463
|
+
{
|
|
464
|
+
name: "init_workspace",
|
|
465
|
+
description: "Get comprehensive instructions for initializing a local Xano development workspace. " +
|
|
466
|
+
"Returns documentation on directory structure, file naming conventions, registry format for tracking changes, " +
|
|
467
|
+
"and workflows for pulling/pushing XanoScript files via the Headless API. " +
|
|
468
|
+
"Use this when setting up local development for Xano projects.",
|
|
469
|
+
inputSchema: {
|
|
470
|
+
type: "object",
|
|
471
|
+
properties: {},
|
|
472
|
+
required: [],
|
|
473
|
+
},
|
|
474
|
+
},
|
|
370
475
|
],
|
|
371
476
|
};
|
|
372
477
|
});
|
|
373
|
-
// Handle tool calls
|
|
374
478
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
375
479
|
if (request.params.name === "api_docs") {
|
|
376
480
|
const args = request.params.arguments;
|
|
@@ -407,16 +511,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
407
511
|
content: [
|
|
408
512
|
{
|
|
409
513
|
type: "text",
|
|
410
|
-
text: "
|
|
514
|
+
text: "XanoScript is valid. No syntax errors found.",
|
|
411
515
|
},
|
|
412
516
|
],
|
|
413
517
|
};
|
|
414
518
|
}
|
|
415
|
-
// Convert parser errors to diagnostics with line/column info
|
|
416
519
|
const diagnostics = parser.errors.map((error) => {
|
|
417
520
|
const startOffset = error.token?.startOffset ?? 0;
|
|
418
521
|
const endOffset = error.token?.endOffset ?? 5;
|
|
419
|
-
// Calculate line and character positions from offset
|
|
420
522
|
const lines = text.substring(0, startOffset).split("\n");
|
|
421
523
|
const line = lines.length - 1;
|
|
422
524
|
const character = lines[lines.length - 1].length;
|
|
@@ -432,7 +534,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
432
534
|
source: error.name || "XanoScript Parser",
|
|
433
535
|
};
|
|
434
536
|
});
|
|
435
|
-
// Format errors for readable output
|
|
436
537
|
const errorMessages = diagnostics.map((d, i) => {
|
|
437
538
|
const location = `Line ${d.range.start.line + 1}, Column ${d.range.start.character + 1}`;
|
|
438
539
|
return `${i + 1}. [${location}] ${d.message}`;
|
|
@@ -462,8 +563,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
462
563
|
}
|
|
463
564
|
if (request.params.name === "xanoscript_docs") {
|
|
464
565
|
const args = request.params.arguments;
|
|
465
|
-
const
|
|
466
|
-
|
|
566
|
+
const documentation = readXanoscriptDocsV2(args);
|
|
567
|
+
return {
|
|
568
|
+
content: [
|
|
569
|
+
{
|
|
570
|
+
type: "text",
|
|
571
|
+
text: documentation,
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
if (request.params.name === "init_workspace") {
|
|
577
|
+
const documentation = generateInitWorkspaceDoc();
|
|
467
578
|
return {
|
|
468
579
|
content: [
|
|
469
580
|
{
|
|
@@ -483,7 +594,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
483
594
|
isError: true,
|
|
484
595
|
};
|
|
485
596
|
});
|
|
486
|
-
//
|
|
597
|
+
// =============================================================================
|
|
598
|
+
// Start Server
|
|
599
|
+
// =============================================================================
|
|
487
600
|
async function main() {
|
|
488
601
|
const transport = new StdioServerTransport();
|
|
489
602
|
await server.connect(transport);
|