@crowley/rag-mcp 1.0.3 → 1.0.4

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.
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Indexing tools module - codebase indexing, status, zero-downtime reindex,
3
3
  * and alias management.
4
+ *
5
+ * index_codebase and reindex_zero_downtime read files locally and upload
6
+ * them to the RAG API in batches via POST /api/index/upload. This allows
7
+ * remote MCP clients to index codebases that aren't on the server filesystem.
4
8
  */
5
9
  import type { ToolModule } from "../types.js";
6
10
  /**
@@ -1,7 +1,105 @@
1
1
  /**
2
2
  * Indexing tools module - codebase indexing, status, zero-downtime reindex,
3
3
  * and alias management.
4
+ *
5
+ * index_codebase and reindex_zero_downtime read files locally and upload
6
+ * them to the RAG API in batches via POST /api/index/upload. This allows
7
+ * remote MCP clients to index codebases that aren't on the server filesystem.
4
8
  */
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ import { glob } from "glob";
12
+ const DEFAULT_PATTERNS = [
13
+ "**/*.ts",
14
+ "**/*.tsx",
15
+ "**/*.js",
16
+ "**/*.jsx",
17
+ "**/*.vue",
18
+ "**/*.py",
19
+ "**/*.go",
20
+ "**/*.rs",
21
+ "**/*.java",
22
+ "**/*.md",
23
+ "**/*.sql",
24
+ "**/*.yml",
25
+ "**/*.yaml",
26
+ "**/Dockerfile",
27
+ ];
28
+ const DEFAULT_EXCLUDE = [
29
+ "**/node_modules/**",
30
+ "**/dist/**",
31
+ "**/build/**",
32
+ "**/.git/**",
33
+ "**/coverage/**",
34
+ "**/.nuxt/**",
35
+ "**/.next/**",
36
+ "**/vendor/**",
37
+ "**/__pycache__/**",
38
+ "**/target/**",
39
+ "**/package-lock.json",
40
+ "**/yarn.lock",
41
+ "**/pnpm-lock.yaml",
42
+ "**/eval/results/**",
43
+ "**/eval/golden-queries.json",
44
+ ];
45
+ const BATCH_SIZE = 50;
46
+ /**
47
+ * Discover files locally using glob, read their contents, and upload
48
+ * them to the RAG API in batches.
49
+ */
50
+ async function uploadFiles(ctx, projectPath, opts) {
51
+ const patterns = opts.patterns || DEFAULT_PATTERNS;
52
+ const excludePatterns = opts.excludePatterns || DEFAULT_EXCLUDE;
53
+ // Discover files locally
54
+ const files = await glob(patterns, {
55
+ cwd: projectPath,
56
+ ignore: excludePatterns,
57
+ nodir: true,
58
+ absolute: false,
59
+ });
60
+ if (files.length === 0) {
61
+ return { totalFiles: 0, indexedFiles: 0, totalChunks: 0, errors: 0, duration: 0 };
62
+ }
63
+ let totalIndexed = 0;
64
+ let totalChunks = 0;
65
+ let totalErrors = 0;
66
+ let totalDuration = 0;
67
+ for (let i = 0; i < files.length; i += BATCH_SIZE) {
68
+ const batch = files.slice(i, i + BATCH_SIZE);
69
+ const filePayloads = [];
70
+ for (const relPath of batch) {
71
+ try {
72
+ const absPath = path.join(projectPath, relPath);
73
+ const content = fs.readFileSync(absPath, "utf-8");
74
+ filePayloads.push({ path: relPath, content });
75
+ }
76
+ catch {
77
+ totalErrors++;
78
+ }
79
+ }
80
+ if (filePayloads.length === 0)
81
+ continue;
82
+ const isFirst = i === 0;
83
+ const isLast = i + BATCH_SIZE >= files.length;
84
+ const response = await ctx.api.post("/api/index/upload", {
85
+ files: filePayloads,
86
+ force: isFirst && (opts.force ?? false),
87
+ done: isLast,
88
+ });
89
+ const data = response.data;
90
+ totalIndexed += data.filesProcessed || 0;
91
+ totalChunks += data.chunksCreated || 0;
92
+ totalErrors += data.errors || 0;
93
+ totalDuration += data.duration || 0;
94
+ }
95
+ return {
96
+ totalFiles: files.length,
97
+ indexedFiles: totalIndexed,
98
+ totalChunks,
99
+ errors: totalErrors,
100
+ duration: totalDuration,
101
+ };
102
+ }
5
103
  /**
6
104
  * Create the indexing tools module with project-specific descriptions.
7
105
  */
@@ -67,16 +165,15 @@ export function createIndexingTools(projectName) {
67
165
  ];
68
166
  const handlers = {
69
167
  index_codebase: async (args, ctx) => {
70
- const { path, force = false } = args;
71
- const response = await ctx.api.post("/api/index", {
72
- collection: `${ctx.collectionPrefix}codebase`,
73
- path: path || ctx.projectPath,
74
- force,
75
- });
76
- const data = response.data;
168
+ const { path: indexPath, force = false } = args;
169
+ const projectPath = indexPath || ctx.projectPath;
170
+ const stats = await uploadFiles(ctx, projectPath, { force });
77
171
  let result = `## Indexing ${projectName}\n\n`;
78
- result += `- **Status:** ${data.status || "started"}\n`;
79
- result += `- **Files to process:** ${data.filesToProcess ?? "N/A"}\n`;
172
+ result += `- **Total files found:** ${stats.totalFiles}\n`;
173
+ result += `- **Files indexed:** ${stats.indexedFiles}\n`;
174
+ result += `- **Chunks created:** ${stats.totalChunks}\n`;
175
+ result += `- **Errors:** ${stats.errors}\n`;
176
+ result += `- **Duration:** ${stats.duration}ms\n`;
80
177
  return result;
81
178
  },
82
179
  get_index_status: async (_args, ctx) => {
@@ -91,18 +188,19 @@ export function createIndexingTools(projectName) {
91
188
  return result;
92
189
  },
93
190
  reindex_zero_downtime: async (args, ctx) => {
94
- const { path, patterns, excludePatterns } = args;
95
- const response = await ctx.api.post("/api/reindex", {
96
- collection: `${ctx.collectionPrefix}codebase`,
97
- path: path || ctx.projectPath,
191
+ const { path: indexPath, patterns, excludePatterns } = args;
192
+ const projectPath = indexPath || ctx.projectPath;
193
+ const stats = await uploadFiles(ctx, projectPath, {
98
194
  patterns,
99
195
  excludePatterns,
196
+ force: true,
100
197
  });
101
- const data = response.data;
102
- let result = `## Zero-Downtime Reindex: ${projectName}\n\n`;
103
- result += `- **Alias:** ${data.alias || "N/A"}\n`;
104
- result += `- **Status:** ${data.status || "started"}\n`;
105
- result += `- **Message:** ${data.message || "Reindex initiated"}\n`;
198
+ let result = `## Reindex: ${projectName}\n\n`;
199
+ result += `- **Total files found:** ${stats.totalFiles}\n`;
200
+ result += `- **Files indexed:** ${stats.indexedFiles}\n`;
201
+ result += `- **Chunks created:** ${stats.totalChunks}\n`;
202
+ result += `- **Errors:** ${stats.errors}\n`;
203
+ result += `- **Duration:** ${stats.duration}ms\n`;
106
204
  return result;
107
205
  },
108
206
  list_aliases: async (_args, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowley/rag-mcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Universal RAG MCP Server for any project",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,8 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@modelcontextprotocol/sdk": "^1.0.0",
34
- "axios": "^1.6.0"
34
+ "axios": "^1.6.0",
35
+ "glob": "^11.0.0"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@types/node": "^20.10.0",