maven-indexer-mcp 1.0.8 → 1.0.9

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/build/index.js CHANGED
@@ -20,6 +20,7 @@ const indexer = Indexer.getInstance();
20
20
  // We trigger indexing but don't await it so server can start
21
21
  indexer.index().then(() => {
22
22
  // Start watching for changes after initial index
23
+ indexer.startSchedule();
23
24
  return indexer.startWatch();
24
25
  }).catch(err => console.error("Initial indexing failed:", err));
25
26
  server.registerTool("get_class_details", {
@@ -154,9 +155,9 @@ server.registerTool("get_class_details", {
154
155
  return { content: [{ type: "text", text: results.join("\n\n") }] };
155
156
  });
156
157
  server.registerTool("search_artifacts", {
157
- description: "Search for internal company artifacts and libraries in the local Maven repository and Gradle caches by coordinate (groupId, artifactId) or keyword. Use this primarily for internal company packages or to find available versions of internal projects that are locally built. Also supports searching third-party libraries in the local cache. Supports batch queries.",
158
+ description: "Search for internal company artifacts and libraries in the local Maven repository and Gradle caches by coordinate (groupId, artifactId), keyword, or class name. Use this primarily for internal company packages or to find available versions of internal projects that are locally built. Also supports searching third-party libraries in the local cache. Supports batch queries.",
158
159
  inputSchema: z.object({
159
- query: z.string().optional().describe("Search query (groupId, artifactId, or keyword)"),
160
+ query: z.string().optional().describe("Search query (groupId, artifactId, keyword, or class name)"),
160
161
  queries: z.array(z.string()).optional().describe("Batch search queries"),
161
162
  }),
162
163
  }, async ({ query, queries }) => {
@@ -169,13 +170,34 @@ server.registerTool("search_artifacts", {
169
170
  return { content: [{ type: "text", text: "No query provided." }] };
170
171
  }
171
172
  const results = allQueries.map(q => {
172
- const matches = indexer.search(q);
173
+ let allMatches = [];
174
+ let searchType = "artifact";
175
+ // First try class search (for both simple and fully qualified class names)
176
+ const classSearchResults = indexer.searchClass(q);
177
+ if (classSearchResults.length > 0) {
178
+ // Extract unique artifacts from class search results
179
+ const artifactMap = new Map();
180
+ classSearchResults.forEach(result => {
181
+ result.artifacts.forEach(artifact => {
182
+ const key = `${artifact.groupId}:${artifact.artifactId}:${artifact.version}`;
183
+ if (!artifactMap.has(key)) {
184
+ artifactMap.set(key, artifact);
185
+ }
186
+ });
187
+ });
188
+ allMatches = Array.from(artifactMap.values());
189
+ searchType = "class";
190
+ }
191
+ else {
192
+ // Fallback to artifact search if class search finds nothing
193
+ allMatches = indexer.search(q);
194
+ }
173
195
  // Limit results to avoid overflow
174
- const limitedMatches = matches.slice(0, 20);
196
+ const limitedMatches = allMatches.slice(0, 20);
175
197
  const text = limitedMatches.length > 0
176
198
  ? limitedMatches.map(a => `${a.groupId}:${a.artifactId}:${a.version} (Has Source: ${a.hasSource})`).join("\n")
177
199
  : "No artifacts found matching the query.";
178
- return `### Results for "${q}" (Found ${matches.length}${matches.length > 20 ? ', showing first 20' : ''}):\n${text}`;
200
+ return `### Results for "${q}" (Found ${allMatches.length} via ${searchType} search${allMatches.length > 20 ? ', showing first 20' : ''}):\n${text}`;
179
201
  });
180
202
  return {
181
203
  content: [
package/build/indexer.js CHANGED
@@ -15,7 +15,7 @@ export class Indexer {
15
15
  isIndexing = false;
16
16
  watcher = null;
17
17
  debounceTimer = null;
18
- pollingTimer = null;
18
+ scheduleTimer = null;
19
19
  constructor() {
20
20
  }
21
21
  static getInstance() {
@@ -25,8 +25,7 @@ export class Indexer {
25
25
  return Indexer.instance;
26
26
  }
27
27
  /**
28
- * Starts watching the local repository for changes - ULTRA SIMPLE VERSION
29
- * Just watch the root directories without recursion
28
+ * Starts watching the local repository for changes.
30
29
  */
31
30
  async startWatch() {
32
31
  const config = await Config.getInstance();
@@ -42,65 +41,73 @@ export class Indexer {
42
41
  console.error("No repository paths found, skipping watch mode.");
43
42
  return;
44
43
  }
45
- if (this.watcher || this.pollingTimer) {
44
+ if (this.watcher) {
46
45
  return;
47
46
  }
48
- console.error(`🔍 Starting ultra-simple file watcher on: ${watchPaths.join(', ')}`);
47
+ console.error(`🔍 Starting file watcher on: ${watchPaths.join(', ')}`);
49
48
  try {
50
- // ULTRA SIMPLE: Watch only root directories, no recursion
49
+ // Watch for .jar and .pom files changes deep in the repository
51
50
  this.watcher = chokidar.watch(watchPaths, {
52
- // Watch only the root directory itself, not subdirectories
53
- depth: 0,
54
- // Don't trigger for files that already exist
51
+ // Watch for jar and pom files
52
+ ignored: [
53
+ /(^|[\/\\])\../, // ignore dotfiles
54
+ /node_modules/,
55
+ /target/,
56
+ /build/,
57
+ ],
58
+ persistent: true,
55
59
  ignoreInitial: true,
56
- // Wait for files to finish writing before triggering
57
60
  awaitWriteFinish: {
58
61
  stabilityThreshold: 2000,
59
62
  pollInterval: 100
60
63
  },
61
- // Don't crash on permission errors
62
64
  ignorePermissionErrors: true
63
65
  });
64
- // SIMPLE: Watch for any changes in the root directories
65
- // This will catch when new directories are created (which means new artifacts)
66
+ // Watch for file additions and changes
66
67
  this.watcher
68
+ .on('add', (filePath) => {
69
+ if (filePath.endsWith('.jar') || filePath.endsWith('.pom')) {
70
+ console.error(`📄 New file detected: ${path.basename(filePath)}`);
71
+ this.triggerReindex();
72
+ }
73
+ })
67
74
  .on('addDir', (dirPath) => {
68
- console.error(`� New directory detected: ${path.basename(dirPath)}`);
75
+ console.error(`📁 New directory detected: ${path.basename(dirPath)}`);
69
76
  this.triggerReindex();
77
+ })
78
+ .on('unlink', (filePath) => {
79
+ if (filePath.endsWith('.jar') || filePath.endsWith('.pom')) {
80
+ console.error(`🗑️ File removed: ${path.basename(filePath)}`);
81
+ this.triggerReindex();
82
+ }
70
83
  })
71
84
  .on('unlinkDir', (dirPath) => {
72
- console.error(`🗑️ Directory removed: ${path.basename(dirPath)}`);
85
+ console.error(`🗑️ Directory removed: ${path.basename(dirPath)}`);
73
86
  this.triggerReindex();
74
87
  })
75
88
  .on('error', (error) => {
76
89
  const errorMessage = error instanceof Error ? error.message : String(error);
77
90
  console.error(`❌ Watcher error: ${errorMessage}`);
78
- this.fallbackToPolling();
79
91
  });
80
- console.error('✅ Ultra-simple file watcher started successfully');
92
+ console.error('✅ File watcher started successfully');
81
93
  }
82
94
  catch (error) {
83
95
  const errorMessage = error instanceof Error ? error.message : String(error);
84
96
  console.error(`❌ Failed to start watcher: ${errorMessage}`);
85
- this.fallbackToPolling();
86
97
  }
87
98
  }
88
99
  /**
89
- * Falls back to polling mode if watcher fails.
90
- * Polls the repository every 1 min
100
+ * Starts a scheduled job to reindex every 1 hour.
91
101
  */
92
- fallbackToPolling() {
93
- if (this.pollingTimer) {
102
+ startSchedule() {
103
+ if (this.scheduleTimer) {
94
104
  return;
95
105
  }
96
- console.error('⚠️ Falling back to polling mode (every 10s)...');
97
- if (this.watcher) {
98
- this.watcher.close().catch(err => console.error(`Error closing watcher: ${err}`));
99
- this.watcher = null;
100
- }
101
- this.pollingTimer = setInterval(() => {
106
+ console.error(' Starting scheduled reindex job (every 1 hour)...');
107
+ this.scheduleTimer = setInterval(() => {
108
+ console.error('⏰ Scheduled reindex triggered...');
102
109
  this.index().catch(console.error);
103
- }, 60000);
110
+ }, 3600000); // 1 hour
104
111
  }
105
112
  /**
106
113
  * Trigger reindexing with debouncing (wait a bit for multiple changes)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maven-indexer-mcp",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "MCP server for indexing local Maven repository",
5
5
  "main": "build/index.js",
6
6
  "type": "module",