maven-indexer-mcp 1.0.5 → 1.0.6
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 +14 -7
- package/build/index.js +202 -153
- package/package.json +1 -1
- package/build/reproduce_issue.js +0 -20
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
A Model Context Protocol (MCP) server that indexes your local Maven repository (`~/.m2/repository`) and Gradle cache (`~/.gradle/caches/modules-2/files-2.1`) to provide AI agents with tools to search for Java classes, method signatures, and source code.
|
|
4
4
|
|
|
5
|
+
**Key Use Case**: While AI models are well-versed in popular public libraries (like Spring, Apache Commons, Guava), they often struggle with:
|
|
6
|
+
1. **Internal Company Packages**: Private libraries that are not public.
|
|
7
|
+
2. **Non-Well-Known Public Packages**: Niche or less popular open-source libraries.
|
|
8
|
+
|
|
9
|
+
This server bridges that gap by allowing the AI to "read" your local dependencies, effectively giving it knowledge of your private and obscure libraries.
|
|
10
|
+
|
|
5
11
|
## Features
|
|
6
12
|
|
|
7
13
|
* **Semantic Class Search**: Search for classes by name (e.g., `StringUtils`) or purpose (e.g., `JsonToXml`).
|
|
@@ -86,21 +92,22 @@ If you prefer to run from source:
|
|
|
86
92
|
|
|
87
93
|
## Available Tools
|
|
88
94
|
|
|
89
|
-
* **`search_classes`**: Search for Java classes in the local Maven repository
|
|
95
|
+
* **`search_classes`**: Search for Java classes in the local Maven repository and Gradle caches.
|
|
90
96
|
* **WHEN TO USE**:
|
|
91
|
-
1. You
|
|
92
|
-
2.
|
|
93
|
-
3. You need to
|
|
97
|
+
1. **Internal/Private Code**: You need to find a class from a company-internal library.
|
|
98
|
+
2. **Obscure Libraries**: You are using a less common public library that the AI doesn't know well.
|
|
99
|
+
3. **Version Verification**: You need to check exactly which version of a class is present locally.
|
|
100
|
+
* *Note*: For well-known libraries (e.g., standard Java lib, Spring), the AI likely knows the class structure already, so this tool is less critical.
|
|
94
101
|
* **Examples**: "Show me the source of StringUtils", "What methods are available on DateTimeUtils?", "Where is this class imported from?".
|
|
95
102
|
* Input: `className` (e.g., "StringUtils", "Json parser")
|
|
96
103
|
* Output: List of matching classes with their artifacts.
|
|
97
104
|
* **`get_class_details`**: Decompile and read the source code of external libraries/dependencies. **Use this instead of 'SearchCodebase' for classes that are imported but defined in JAR files.**
|
|
98
|
-
* **Key Value**: "Don't guess what the library does—read the code."
|
|
99
|
-
* **Tip**:
|
|
105
|
+
* **Key Value**: "Don't guess what the internal library does—read the code."
|
|
106
|
+
* **Tip**: Essential for internal/proprietary code where documentation is scarce or non-existent.
|
|
100
107
|
* Input: `className` (required), `artifactId` (optional), `type` ("signatures", "docs", "source")
|
|
101
108
|
* Output: Method signatures, Javadocs, or full source code.
|
|
102
109
|
* **Note**: If `artifactId` is omitted, the tool automatically selects the best available artifact (preferring those with source code attached).
|
|
103
|
-
* **`search_artifacts`**: Search for artifacts by coordinate (groupId, artifactId).
|
|
110
|
+
* **`search_artifacts`**: Search for artifacts in Maven/Gradle caches by coordinate (groupId, artifactId).
|
|
104
111
|
* **`search_implementations`**: Search for classes that implement a specific interface or extend a specific class. Useful for finding SPI implementations in external libraries.
|
|
105
112
|
* Input: `className` (e.g. "java.util.List")
|
|
106
113
|
* Output: List of implementation/subclass names and their artifacts.
|
package/build/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const server = new McpServer({
|
|
|
11
11
|
}, {
|
|
12
12
|
capabilities: {
|
|
13
13
|
tools: {},
|
|
14
|
+
prompts: {},
|
|
14
15
|
},
|
|
15
16
|
});
|
|
16
17
|
// Start indexing in the background
|
|
@@ -20,188 +21,236 @@ indexer.index().then(() => {
|
|
|
20
21
|
// Start watching for changes after initial index
|
|
21
22
|
return indexer.startWatch();
|
|
22
23
|
}).catch(err => console.error("Initial indexing failed:", err));
|
|
23
|
-
server.registerTool("search_artifacts", {
|
|
24
|
-
description: "Search for Maven artifacts (libraries) in the local repository by coordinate (groupId, artifactId) or keyword. Use this to find available versions of a library.",
|
|
25
|
-
inputSchema: z.object({
|
|
26
|
-
query: z.string().describe("Search query (groupId, artifactId, or keyword)"),
|
|
27
|
-
}),
|
|
28
|
-
}, async ({ query }) => {
|
|
29
|
-
const matches = indexer.search(query);
|
|
30
|
-
// Limit results to avoid overflow
|
|
31
|
-
const limitedMatches = matches.slice(0, 20);
|
|
32
|
-
const text = limitedMatches.length > 0
|
|
33
|
-
? limitedMatches.map(a => `${a.groupId}:${a.artifactId}:${a.version} (Has Source: ${a.hasSource})`).join("\n")
|
|
34
|
-
: "No artifacts found matching the query.";
|
|
35
|
-
return {
|
|
36
|
-
content: [
|
|
37
|
-
{
|
|
38
|
-
type: "text",
|
|
39
|
-
text: `Found ${matches.length} matches${matches.length > 20 ? ' (showing first 20)' : ''}:\n${text}`,
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
server.registerTool("search_classes", {
|
|
45
|
-
description: "Search for Java classes in the local Maven repository. WHEN TO USE: 1. You cannot find a class definition in the current project source (it's likely a dependency). 2. You need to read the source code, method signatures, or Javadocs of an external library class. 3. You need to verify which version of a library class is being used. Examples: 'Show me the source of StringUtils', 'What methods are available on DateTimeUtils?', 'Where is this class imported from?'.",
|
|
46
|
-
inputSchema: z.object({
|
|
47
|
-
className: z.string().describe("Fully qualified class name, partial name, or keywords describing the class purpose (e.g. 'JsonToXml')."),
|
|
48
|
-
}),
|
|
49
|
-
}, async ({ className }) => {
|
|
50
|
-
const matches = indexer.searchClass(className);
|
|
51
|
-
const text = matches.length > 0
|
|
52
|
-
? matches.map(m => {
|
|
53
|
-
// Group by artifact ID to allow easy selection
|
|
54
|
-
const artifacts = m.artifacts.slice(0, 5).map(a => `${a.groupId}:${a.artifactId}:${a.version}${a.hasSource ? ' (Has Source)' : ''}`).join("\n ");
|
|
55
|
-
const more = m.artifacts.length > 5 ? `\n ... (${m.artifacts.length - 5} more versions)` : '';
|
|
56
|
-
return `Class: ${m.className}\n ${artifacts}${more}`;
|
|
57
|
-
}).join("\n\n")
|
|
58
|
-
: "No classes found matching the query. Try different keywords.";
|
|
59
|
-
return {
|
|
60
|
-
content: [{ type: "text", text }]
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
server.registerTool("search_implementations", {
|
|
64
|
-
description: "Search for classes that implement a specific interface or extend a specific class. This is useful for finding implementations of SPIs or base classes, especially in external libraries.",
|
|
65
|
-
inputSchema: z.object({
|
|
66
|
-
className: z.string().describe("Fully qualified class name of the interface or base class (e.g. 'java.util.List')"),
|
|
67
|
-
}),
|
|
68
|
-
}, async ({ className }) => {
|
|
69
|
-
const matches = indexer.searchImplementations(className);
|
|
70
|
-
const text = matches.length > 0
|
|
71
|
-
? matches.map(m => {
|
|
72
|
-
const artifacts = m.artifacts.slice(0, 5).map(a => `${a.groupId}:${a.artifactId}:${a.version}`).join("\n ");
|
|
73
|
-
const more = m.artifacts.length > 5 ? `\n ... (${m.artifacts.length - 5} more versions)` : '';
|
|
74
|
-
return `Implementation: ${m.className}\n ${artifacts}${more}`;
|
|
75
|
-
}).join("\n\n")
|
|
76
|
-
: `No implementations found for ${className}. Ensure the index is up to date and the class name is correct.`;
|
|
77
|
-
return {
|
|
78
|
-
content: [{ type: "text", text }]
|
|
79
|
-
};
|
|
80
|
-
});
|
|
81
24
|
server.registerTool("get_class_details", {
|
|
82
|
-
description: "
|
|
25
|
+
description: "Retrieve the source code for a class from the **local Maven/Gradle cache** (containing **internal company libraries**). This tool identifies the containing artifact and returns the source code. It prefers actual source files but will fall back to decompilation if necessary. **Use this primarily for internal company libraries** that are not present in the current workspace. **IMPORTANT: Even if the code compiles and imports work, the source code might not be in the current workspace (it comes from a compiled internal library).** Use this tool to see the actual implementation of those internal libraries. Supports batch queries.",
|
|
83
26
|
inputSchema: z.object({
|
|
84
|
-
className: z.string().describe("Fully qualified class name"),
|
|
85
|
-
|
|
27
|
+
className: z.string().optional().describe("Fully qualified class name"),
|
|
28
|
+
classNames: z.array(z.string()).optional().describe("Batch class names"),
|
|
29
|
+
coordinate: z.string().optional().describe("The Maven coordinate of the artifact (groupId:artifactId:version). Optional: if not provided, the tool will automatically find the best match (preferring artifacts with source code). Applies to all classes in batch mode."),
|
|
86
30
|
type: z.enum(["signatures", "docs", "source"]).describe("Type of detail to retrieve: 'signatures' (methods), 'docs' (javadocs + methods), 'source' (full source code)."),
|
|
87
31
|
}),
|
|
88
|
-
}, async ({ className, coordinate, type }) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
32
|
+
}, async ({ className, classNames, coordinate, type }) => {
|
|
33
|
+
const resolveOne = async (clsName, coord) => {
|
|
34
|
+
let targetArtifact;
|
|
35
|
+
if (coord) {
|
|
36
|
+
const parts = coord.split(':');
|
|
37
|
+
if (parts.length === 3) {
|
|
38
|
+
targetArtifact = indexer.getArtifactByCoordinate(parts[0], parts[1], parts[2]);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return "Invalid coordinate format. Expected groupId:artifactId:version";
|
|
42
|
+
}
|
|
43
|
+
if (!targetArtifact) {
|
|
44
|
+
return `Artifact ${coord} not found in index.`;
|
|
45
|
+
}
|
|
94
46
|
}
|
|
95
47
|
else {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!exactMatch) {
|
|
108
|
-
// If no exact match, but we have some results, list them
|
|
109
|
-
if (matches.length > 0) {
|
|
110
|
-
const suggestions = matches.map(m => `- ${m.className}`).join("\n");
|
|
111
|
-
return { content: [{ type: "text", text: `Class '${className}' not found exactly. Did you mean:\n${suggestions}` }] };
|
|
48
|
+
// Auto-resolve artifact if coordinate is missing
|
|
49
|
+
const matches = indexer.searchClass(clsName);
|
|
50
|
+
// Find exact match for class name
|
|
51
|
+
const exactMatch = matches.find(m => m.className === clsName);
|
|
52
|
+
if (!exactMatch) {
|
|
53
|
+
// If no exact match, but we have some results, list them
|
|
54
|
+
if (matches.length > 0) {
|
|
55
|
+
const suggestions = matches.map(m => `- ${m.className}`).join("\n");
|
|
56
|
+
return `Class '${clsName}' not found exactly. Did you mean:\n${suggestions}`;
|
|
57
|
+
}
|
|
58
|
+
return `Class '${clsName}' not found in the index. Try 'search_classes' with a keyword if you are unsure of the full name.`;
|
|
112
59
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return
|
|
60
|
+
// We have an exact match, choose the best artifact
|
|
61
|
+
// Strategy: 1. Prefer hasSource=true. 2. Prefer highest ID (likely newest).
|
|
62
|
+
const artifacts = exactMatch.artifacts.sort((a, b) => {
|
|
63
|
+
if (a.hasSource !== b.hasSource) {
|
|
64
|
+
return a.hasSource ? -1 : 1; // source comes first
|
|
65
|
+
}
|
|
66
|
+
return b.id - a.id; // higher ID comes first
|
|
67
|
+
});
|
|
68
|
+
if (artifacts.length === 0) {
|
|
69
|
+
return `Class '${clsName}' found but no artifacts are associated with it (database inconsistency).`;
|
|
120
70
|
}
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
if (artifacts.length === 0) {
|
|
124
|
-
return { content: [{ type: "text", text: `Class '${className}' found but no artifacts are associated with it (database inconsistency).` }] };
|
|
71
|
+
targetArtifact = artifacts[0];
|
|
125
72
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
73
|
+
const artifact = targetArtifact;
|
|
74
|
+
let detail = null;
|
|
75
|
+
let usedDecompilation = false;
|
|
76
|
+
let lastError = "";
|
|
77
|
+
// 1. If requesting source/docs, try Source JAR first
|
|
78
|
+
if (type === 'source' || type === 'docs') {
|
|
79
|
+
if (artifact.hasSource) {
|
|
80
|
+
const sourceJarPath = path.join(artifact.abspath, `${artifact.artifactId}-${artifact.version}-sources.jar`);
|
|
81
|
+
try {
|
|
82
|
+
detail = await SourceParser.getClassDetail(sourceJarPath, clsName, type);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
// Ignore error and fallthrough to main jar (decompilation)
|
|
86
|
+
lastError = e.message;
|
|
87
|
+
}
|
|
139
88
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
89
|
+
// If not found in source jar (or no source jar), try main jar (decompilation)
|
|
90
|
+
if (!detail) {
|
|
91
|
+
let mainJarPath = artifact.abspath;
|
|
92
|
+
if (!mainJarPath.endsWith('.jar')) {
|
|
93
|
+
mainJarPath = path.join(artifact.abspath, `${artifact.artifactId}-${artifact.version}.jar`);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
// SourceParser will try to decompile if source file not found in jar
|
|
97
|
+
detail = await SourceParser.getClassDetail(mainJarPath, clsName, type);
|
|
98
|
+
if (detail && detail.source) {
|
|
99
|
+
usedDecompilation = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
console.error(`Decompilation/MainJar access failed: ${e.message}`);
|
|
104
|
+
lastError = e.message;
|
|
105
|
+
}
|
|
143
106
|
}
|
|
144
107
|
}
|
|
145
|
-
|
|
146
|
-
|
|
108
|
+
else {
|
|
109
|
+
// Signatures -> Use Main JAR
|
|
147
110
|
let mainJarPath = artifact.abspath;
|
|
148
111
|
if (!mainJarPath.endsWith('.jar')) {
|
|
149
112
|
mainJarPath = path.join(artifact.abspath, `${artifact.artifactId}-${artifact.version}.jar`);
|
|
150
113
|
}
|
|
151
114
|
try {
|
|
152
|
-
|
|
153
|
-
detail = await SourceParser.getClassDetail(mainJarPath, className, type);
|
|
154
|
-
if (detail && detail.source) {
|
|
155
|
-
usedDecompilation = true;
|
|
156
|
-
}
|
|
115
|
+
detail = await SourceParser.getClassDetail(mainJarPath, clsName, type);
|
|
157
116
|
}
|
|
158
117
|
catch (e) {
|
|
159
|
-
console.error(`Decompilation/MainJar access failed: ${e.message}`);
|
|
160
118
|
lastError = e.message;
|
|
161
119
|
}
|
|
162
120
|
}
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
// Signatures -> Use Main JAR
|
|
166
|
-
let mainJarPath = artifact.abspath;
|
|
167
|
-
if (!mainJarPath.endsWith('.jar')) {
|
|
168
|
-
mainJarPath = path.join(artifact.abspath, `${artifact.artifactId}-${artifact.version}.jar`);
|
|
169
|
-
}
|
|
170
121
|
try {
|
|
171
|
-
|
|
122
|
+
if (!detail) {
|
|
123
|
+
const debugInfo = `Artifact path: ${artifact.abspath}, hasSource: ${artifact.hasSource}`;
|
|
124
|
+
const errorMsg = lastError ? `\nLast error: ${lastError}` : "";
|
|
125
|
+
return `Class ${clsName} not found in artifact ${artifact.artifactId}. \nDebug info: ${debugInfo}${errorMsg}`;
|
|
126
|
+
}
|
|
127
|
+
let resultText = `### Class: ${detail.className}\n`;
|
|
128
|
+
resultText += `Artifact: ${artifact.groupId}:${artifact.artifactId}:${artifact.version}\n\n`; // Inform user which artifact was used
|
|
129
|
+
if (usedDecompilation) {
|
|
130
|
+
resultText += "*Source code decompiled from binary class file.*\n\n";
|
|
131
|
+
}
|
|
132
|
+
if (type === 'source') {
|
|
133
|
+
const lang = detail.language || 'java';
|
|
134
|
+
resultText += "```" + lang + "\n" + detail.source + "\n```";
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
if (detail.doc) {
|
|
138
|
+
resultText += "Documentation:\n" + detail.doc + "\n\n";
|
|
139
|
+
}
|
|
140
|
+
if (detail.signatures) {
|
|
141
|
+
resultText += "Methods:\n" + detail.signatures.join("\n") + "\n";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return resultText;
|
|
172
145
|
}
|
|
173
146
|
catch (e) {
|
|
174
|
-
|
|
147
|
+
return `Error reading source: ${e.message}`;
|
|
175
148
|
}
|
|
149
|
+
};
|
|
150
|
+
const allNames = [];
|
|
151
|
+
if (className)
|
|
152
|
+
allNames.push(className);
|
|
153
|
+
if (classNames)
|
|
154
|
+
allNames.push(...classNames);
|
|
155
|
+
if (allNames.length === 0) {
|
|
156
|
+
return { content: [{ type: "text", text: "No class name provided." }] };
|
|
176
157
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
resultText += "Documentation:\n" + detail.doc + "\n\n";
|
|
195
|
-
}
|
|
196
|
-
if (detail.signatures) {
|
|
197
|
-
resultText += "Methods:\n" + detail.signatures.join("\n") + "\n";
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return { content: [{ type: "text", text: resultText }] };
|
|
158
|
+
const results = await Promise.all(allNames.map(name => resolveOne(name, coordinate)));
|
|
159
|
+
return { content: [{ type: "text", text: results.join("\n\n") }] };
|
|
160
|
+
});
|
|
161
|
+
server.registerTool("search_artifacts", {
|
|
162
|
+
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.",
|
|
163
|
+
inputSchema: z.object({
|
|
164
|
+
query: z.string().optional().describe("Search query (groupId, artifactId, or keyword)"),
|
|
165
|
+
queries: z.array(z.string()).optional().describe("Batch search queries"),
|
|
166
|
+
}),
|
|
167
|
+
}, async ({ query, queries }) => {
|
|
168
|
+
const allQueries = [];
|
|
169
|
+
if (query)
|
|
170
|
+
allQueries.push(query);
|
|
171
|
+
if (queries)
|
|
172
|
+
allQueries.push(...queries);
|
|
173
|
+
if (allQueries.length === 0) {
|
|
174
|
+
return { content: [{ type: "text", text: "No query provided." }] };
|
|
201
175
|
}
|
|
202
|
-
|
|
203
|
-
|
|
176
|
+
const results = allQueries.map(q => {
|
|
177
|
+
const matches = indexer.search(q);
|
|
178
|
+
// Limit results to avoid overflow
|
|
179
|
+
const limitedMatches = matches.slice(0, 20);
|
|
180
|
+
const text = limitedMatches.length > 0
|
|
181
|
+
? limitedMatches.map(a => `${a.groupId}:${a.artifactId}:${a.version} (Has Source: ${a.hasSource})`).join("\n")
|
|
182
|
+
: "No artifacts found matching the query.";
|
|
183
|
+
return `### Results for "${q}" (Found ${matches.length}${matches.length > 20 ? ', showing first 20' : ''}):\n${text}`;
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: results.join("\n\n"),
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
server.registerTool("search_classes", {
|
|
195
|
+
description: "Search for Java classes in **internal company libraries** found in the local Maven/Gradle caches. **Essential for finding classes in internal company libraries** that are not part of the current workspace source code. Use this when you see an import (e.g., 'com.company.util.Helper') but cannot find the definition. **Do not assume that because the code compiles or the import exists, the source is local.** It often comes from a compiled **internal library**. This tool helps locate the defining artifact. Supports batch queries.",
|
|
196
|
+
inputSchema: z.object({
|
|
197
|
+
className: z.string().optional().describe("Fully qualified class name, partial name, or keywords describing the class purpose (e.g. 'JsonToXml')."),
|
|
198
|
+
classNames: z.array(z.string()).optional().describe("Batch class names"),
|
|
199
|
+
}),
|
|
200
|
+
}, async ({ className, classNames }) => {
|
|
201
|
+
const allNames = [];
|
|
202
|
+
if (className)
|
|
203
|
+
allNames.push(className);
|
|
204
|
+
if (classNames)
|
|
205
|
+
allNames.push(...classNames);
|
|
206
|
+
if (allNames.length === 0) {
|
|
207
|
+
return { content: [{ type: "text", text: "No class name provided." }] };
|
|
208
|
+
}
|
|
209
|
+
const results = allNames.map(name => {
|
|
210
|
+
const matches = indexer.searchClass(name);
|
|
211
|
+
const text = matches.length > 0
|
|
212
|
+
? matches.map(m => {
|
|
213
|
+
// Group by artifact ID to allow easy selection
|
|
214
|
+
const artifacts = m.artifacts.slice(0, 5).map(a => `${a.groupId}:${a.artifactId}:${a.version}${a.hasSource ? ' (Has Source)' : ''}`).join("\n ");
|
|
215
|
+
const more = m.artifacts.length > 5 ? `\n ... (${m.artifacts.length - 5} more versions)` : '';
|
|
216
|
+
return `Class: ${m.className}\n ${artifacts}${more}`;
|
|
217
|
+
}).join("\n\n")
|
|
218
|
+
: "No classes found matching the query. Try different keywords.";
|
|
219
|
+
return `### Results for "${name}":\n${text}`;
|
|
220
|
+
});
|
|
221
|
+
return {
|
|
222
|
+
content: [{ type: "text", text: results.join("\n\n") }]
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
server.registerTool("search_implementations", {
|
|
226
|
+
description: "Search for **internal implementations** of an interface or base class. **This is particularly useful for finding implementations of SPIs or base classes within internal company libraries** in the local Maven/Gradle cache. Supports batch queries.",
|
|
227
|
+
inputSchema: z.object({
|
|
228
|
+
className: z.string().optional().describe("Fully qualified class name of the interface or base class (e.g. 'java.util.List')"),
|
|
229
|
+
classNames: z.array(z.string()).optional().describe("Batch class names"),
|
|
230
|
+
}),
|
|
231
|
+
}, async ({ className, classNames }) => {
|
|
232
|
+
const allNames = [];
|
|
233
|
+
if (className)
|
|
234
|
+
allNames.push(className);
|
|
235
|
+
if (classNames)
|
|
236
|
+
allNames.push(...classNames);
|
|
237
|
+
if (allNames.length === 0) {
|
|
238
|
+
return { content: [{ type: "text", text: "No class name provided." }] };
|
|
204
239
|
}
|
|
240
|
+
const results = allNames.map(name => {
|
|
241
|
+
const matches = indexer.searchImplementations(name);
|
|
242
|
+
const text = matches.length > 0
|
|
243
|
+
? matches.map(m => {
|
|
244
|
+
const artifacts = m.artifacts.slice(0, 5).map(a => `${a.groupId}:${a.artifactId}:${a.version}`).join("\n ");
|
|
245
|
+
const more = m.artifacts.length > 5 ? `\n ... (${m.artifacts.length - 5} more versions)` : '';
|
|
246
|
+
return `Implementation: ${m.className}\n ${artifacts}${more}`;
|
|
247
|
+
}).join("\n\n")
|
|
248
|
+
: `No implementations found for ${name}. Ensure the index is up to date and the class name is correct.`;
|
|
249
|
+
return `### Results for "${name}":\n${text}`;
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
content: [{ type: "text", text: results.join("\n\n") }]
|
|
253
|
+
};
|
|
205
254
|
});
|
|
206
255
|
server.registerTool("refresh_index", {
|
|
207
256
|
description: "Trigger a re-scan of the Maven repository. This will re-index all artifacts.",
|
package/package.json
CHANGED
package/build/reproduce_issue.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Indexer } from "./indexer.js";
|
|
2
|
-
import { DB } from "./db/index.js";
|
|
3
|
-
async function main() {
|
|
4
|
-
const db = DB.getInstance();
|
|
5
|
-
const indexer = Indexer.getInstance();
|
|
6
|
-
// We assume indexing is done since we just ran it.
|
|
7
|
-
console.log("Searching for 'Kotlin'...");
|
|
8
|
-
const kotlinResults = db.prepare("SELECT class_name FROM classes_fts WHERE class_name LIKE '%Kotlin%' LIMIT 10").all();
|
|
9
|
-
console.log("First 10 Kotlin classes:", kotlinResults);
|
|
10
|
-
console.log("Searching for 'AnAction'...");
|
|
11
|
-
const anActionResults = indexer.searchClass("AnAction");
|
|
12
|
-
console.log(`Found ${anActionResults.length} results for 'AnAction'.`);
|
|
13
|
-
if (anActionResults.length > 0) {
|
|
14
|
-
console.log(anActionResults[0]);
|
|
15
|
-
}
|
|
16
|
-
console.log("Searching for 'StringUtils'...");
|
|
17
|
-
const stringUtilsResults = indexer.searchClass("StringUtils");
|
|
18
|
-
console.log(`Found ${stringUtilsResults.length} results for 'StringUtils'.`);
|
|
19
|
-
}
|
|
20
|
-
main().catch(console.error);
|