semantic-code-mcp 2.1.1 → 2.2.0
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/features/hybrid-search.js +24 -15
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ export class HybridSearch {
|
|
|
9
9
|
this.indexer = indexer; // Reference to indexer for status checking
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async search(query, maxResults) {
|
|
12
|
+
async search(query, maxResults, scopePath = "") {
|
|
13
13
|
const hasAnnSearch = typeof this.cache?.searchByVector === "function";
|
|
14
14
|
|
|
15
15
|
// Show warning if indexing is still in progress but we have some results
|
|
@@ -22,9 +22,12 @@ export class HybridSearch {
|
|
|
22
22
|
const queryEmbed = await this.embedder(query, { pooling: "mean", normalize: true });
|
|
23
23
|
const queryVector = Array.from(queryEmbed.data);
|
|
24
24
|
|
|
25
|
+
// Build Milvus filter for scoped search
|
|
26
|
+
const filter = scopePath ? `file like '${scopePath.replace(/'/g, "\\'")}%'` : null;
|
|
27
|
+
|
|
25
28
|
if (hasAnnSearch) {
|
|
26
29
|
const annTopK = Math.max(maxResults * 5, 20);
|
|
27
|
-
const candidates = await this.cache.searchByVector(queryVector, annTopK);
|
|
30
|
+
const candidates = await this.cache.searchByVector(queryVector, annTopK, filter);
|
|
28
31
|
|
|
29
32
|
const scoredChunks = candidates.map((chunk) => {
|
|
30
33
|
// Base semantic score from provider (Milvus or fallback cache) plus lexical boost.
|
|
@@ -124,11 +127,11 @@ export class HybridSearch {
|
|
|
124
127
|
return results.map((r, idx) => {
|
|
125
128
|
const relPath = path.relative(this.config.searchDirectory, r.file);
|
|
126
129
|
return `## Result ${idx + 1} (Relevance: ${(r.score * 100).toFixed(1)}%)\n` +
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
`**File:** \`${relPath}\`\n` +
|
|
131
|
+
`**Lines:** ${r.startLine}-${r.endLine}\n\n` +
|
|
132
|
+
"```" + path.extname(r.file).slice(1) + "\n" +
|
|
133
|
+
r.content + "\n" +
|
|
134
|
+
"```\n";
|
|
132
135
|
}).join("\n");
|
|
133
136
|
}
|
|
134
137
|
}
|
|
@@ -141,14 +144,19 @@ export function getToolDefinition(config) {
|
|
|
141
144
|
inputSchema: {
|
|
142
145
|
type: "object",
|
|
143
146
|
properties: {
|
|
144
|
-
query: {
|
|
145
|
-
type: "string",
|
|
146
|
-
description: "Search query - can be natural language (e.g., 'where do we handle user login') or specific terms"
|
|
147
|
+
query: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "Search query - can be natural language (e.g., 'where do we handle user login') or specific terms"
|
|
147
150
|
},
|
|
148
151
|
maxResults: {
|
|
149
152
|
type: "number",
|
|
150
153
|
description: "Maximum number of results to return (default: from config)",
|
|
151
154
|
default: config.maxResults
|
|
155
|
+
},
|
|
156
|
+
scopePath: {
|
|
157
|
+
type: "string",
|
|
158
|
+
description: "Limit search to files under this absolute path prefix (e.g., '/path/to/subfolder'). Empty string searches all.",
|
|
159
|
+
default: ""
|
|
152
160
|
}
|
|
153
161
|
},
|
|
154
162
|
required: ["query"]
|
|
@@ -167,9 +175,10 @@ export function getToolDefinition(config) {
|
|
|
167
175
|
export async function handleToolCall(request, hybridSearch) {
|
|
168
176
|
const query = request.params.arguments.query;
|
|
169
177
|
const maxResults = request.params.arguments.maxResults || hybridSearch.config.maxResults;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
const scopePath = request.params.arguments.scopePath || "";
|
|
179
|
+
|
|
180
|
+
const { results, message, indexingWarning } = await hybridSearch.search(query, maxResults, scopePath);
|
|
181
|
+
|
|
173
182
|
if (message) {
|
|
174
183
|
return {
|
|
175
184
|
content: [{ type: "text", text: message }]
|
|
@@ -177,12 +186,12 @@ export async function handleToolCall(request, hybridSearch) {
|
|
|
177
186
|
}
|
|
178
187
|
|
|
179
188
|
let formattedText = hybridSearch.formatResults(results);
|
|
180
|
-
|
|
189
|
+
|
|
181
190
|
// Prepend indexing warning if present
|
|
182
191
|
if (indexingWarning) {
|
|
183
192
|
formattedText = indexingWarning + formattedText;
|
|
184
193
|
}
|
|
185
|
-
|
|
194
|
+
|
|
186
195
|
return {
|
|
187
196
|
content: [{ type: "text", text: formattedText }]
|
|
188
197
|
};
|
package/package.json
CHANGED