@mhalder/qdrant-mcp-server 3.2.0 → 3.3.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/.dagger/.gitattributes +1 -0
- package/.dagger/package.json +6 -0
- package/.dagger/src/index.ts +83 -0
- package/.dagger/tsconfig.json +13 -0
- package/.dagger/yarn.lock +8 -0
- package/.github/workflows/ci.yml +17 -27
- package/.github/workflows/release.yml +16 -19
- package/CHANGELOG.md +13 -0
- package/README.md +11 -9
- package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -1
- package/build/code/chunker/tree-sitter-chunker.js +15 -3
- package/build/code/chunker/tree-sitter-chunker.js.map +1 -1
- package/build/code/indexer.d.ts +1 -0
- package/build/code/indexer.d.ts.map +1 -1
- package/build/code/indexer.js +24 -4
- package/build/code/indexer.js.map +1 -1
- package/build/embeddings/cohere.d.ts +1 -0
- package/build/embeddings/cohere.d.ts.map +1 -1
- package/build/embeddings/cohere.js +8 -1
- package/build/embeddings/cohere.js.map +1 -1
- package/build/embeddings/cohere.test.js +11 -0
- package/build/embeddings/cohere.test.js.map +1 -1
- package/build/embeddings/factory.d.ts.map +1 -1
- package/build/embeddings/factory.js +2 -0
- package/build/embeddings/factory.js.map +1 -1
- package/build/embeddings/factory.test.js +12 -1
- package/build/embeddings/factory.test.js.map +1 -1
- package/build/embeddings/ollama.d.ts +1 -0
- package/build/embeddings/ollama.d.ts.map +1 -1
- package/build/embeddings/ollama.js +8 -1
- package/build/embeddings/ollama.js.map +1 -1
- package/build/embeddings/ollama.test.js +11 -0
- package/build/embeddings/ollama.test.js.map +1 -1
- package/build/embeddings/openai.d.ts +1 -0
- package/build/embeddings/openai.d.ts.map +1 -1
- package/build/embeddings/openai.js +8 -1
- package/build/embeddings/openai.js.map +1 -1
- package/build/embeddings/openai.test.js +11 -0
- package/build/embeddings/openai.test.js.map +1 -1
- package/build/embeddings/voyage.d.ts +1 -0
- package/build/embeddings/voyage.d.ts.map +1 -1
- package/build/embeddings/voyage.js +8 -1
- package/build/embeddings/voyage.js.map +1 -1
- package/build/embeddings/voyage.test.js +11 -0
- package/build/embeddings/voyage.test.js.map +1 -1
- package/build/git/indexer.d.ts +1 -0
- package/build/git/indexer.d.ts.map +1 -1
- package/build/git/indexer.js +16 -3
- package/build/git/indexer.js.map +1 -1
- package/build/git/indexer.test.js +15 -9
- package/build/git/indexer.test.js.map +1 -1
- package/build/index.js +35 -26
- package/build/index.js.map +1 -1
- package/build/index.test.js +105 -91
- package/build/index.test.js.map +1 -1
- package/build/logger.d.ts +4 -0
- package/build/logger.d.ts.map +1 -0
- package/build/logger.js +24 -0
- package/build/logger.js.map +1 -0
- package/build/qdrant/client.d.ts +1 -0
- package/build/qdrant/client.d.ts.map +1 -1
- package/build/qdrant/client.js +10 -0
- package/build/qdrant/client.js.map +1 -1
- package/build/qdrant/client.test.js +11 -0
- package/build/qdrant/client.test.js.map +1 -1
- package/build/tools/code.d.ts.map +1 -1
- package/build/tools/code.js +44 -13
- package/build/tools/code.js.map +1 -1
- package/build/tools/collection.d.ts.map +1 -1
- package/build/tools/collection.js +15 -8
- package/build/tools/collection.js.map +1 -1
- package/build/tools/document.d.ts.map +1 -1
- package/build/tools/document.js +9 -4
- package/build/tools/document.js.map +1 -1
- package/build/tools/federated.d.ts.map +1 -1
- package/build/tools/federated.js +9 -4
- package/build/tools/federated.js.map +1 -1
- package/build/tools/federated.test.js +11 -0
- package/build/tools/federated.test.js.map +1 -1
- package/build/tools/git-history.d.ts.map +1 -1
- package/build/tools/git-history.js +44 -12
- package/build/tools/git-history.js.map +1 -1
- package/build/tools/logging.d.ts +16 -0
- package/build/tools/logging.d.ts.map +1 -0
- package/build/tools/logging.js +68 -0
- package/build/tools/logging.js.map +1 -0
- package/build/tools/logging.test.d.ts +2 -0
- package/build/tools/logging.test.d.ts.map +1 -0
- package/build/tools/logging.test.js +139 -0
- package/build/tools/logging.test.js.map +1 -0
- package/build/tools/schemas.d.ts +32 -19
- package/build/tools/schemas.d.ts.map +1 -1
- package/build/tools/schemas.js +9 -3
- package/build/tools/schemas.js.map +1 -1
- package/build/tools/search.d.ts.map +1 -1
- package/build/tools/search.js +13 -4
- package/build/tools/search.js.map +1 -1
- package/dagger.json +8 -0
- package/mise.toml +2 -0
- package/package.json +14 -13
- package/src/code/chunker/tree-sitter-chunker.ts +41 -9
- package/src/code/indexer.ts +41 -6
- package/src/embeddings/cohere.test.ts +12 -0
- package/src/embeddings/cohere.ts +10 -2
- package/src/embeddings/factory.test.ts +13 -1
- package/src/embeddings/factory.ts +3 -0
- package/src/embeddings/ollama.test.ts +12 -0
- package/src/embeddings/ollama.ts +10 -2
- package/src/embeddings/openai.test.ts +12 -0
- package/src/embeddings/openai.ts +10 -2
- package/src/embeddings/voyage.test.ts +12 -0
- package/src/embeddings/voyage.ts +10 -2
- package/src/git/indexer.test.ts +22 -16
- package/src/git/indexer.ts +30 -4
- package/src/index.test.ts +128 -106
- package/src/index.ts +59 -38
- package/src/logger.ts +33 -0
- package/src/qdrant/client.test.ts +12 -0
- package/src/qdrant/client.ts +22 -0
- package/src/tools/code.ts +107 -62
- package/src/tools/collection.ts +39 -22
- package/src/tools/document.ts +52 -22
- package/src/tools/federated.test.ts +12 -0
- package/src/tools/federated.ts +143 -125
- package/src/tools/git-history.ts +117 -60
- package/src/tools/logging.test.ts +206 -0
- package/src/tools/logging.ts +85 -0
- package/src/tools/schemas.ts +9 -3
- package/src/tools/search.ts +93 -71
- package/tests/code/chunker/tree-sitter-chunker.test.ts +13 -1
- package/tests/code/indexer.test.ts +12 -0
- package/tests/code/integration.test.ts +14 -1
package/src/tools/collection.ts
CHANGED
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import logger from "../logger.js";
|
|
6
7
|
import type { EmbeddingProvider } from "../embeddings/base.js";
|
|
7
8
|
import type { QdrantManager } from "../qdrant/client.js";
|
|
9
|
+
import { withToolLogging } from "./logging.js";
|
|
8
10
|
import * as schemas from "./schemas.js";
|
|
9
11
|
|
|
12
|
+
const log = logger.child({ component: "tools" });
|
|
13
|
+
|
|
10
14
|
export interface CollectionToolDependencies {
|
|
11
15
|
qdrant: QdrantManager;
|
|
12
16
|
embeddings: EmbeddingProvider;
|
|
@@ -27,24 +31,31 @@ export function registerCollectionTools(
|
|
|
27
31
|
"Create a new vector collection in Qdrant. The collection will be configured with the embedding provider's dimensions automatically. Set enableHybrid to true to enable hybrid search combining semantic and keyword search.",
|
|
28
32
|
inputSchema: schemas.CreateCollectionSchema,
|
|
29
33
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
withToolLogging(
|
|
35
|
+
"create_collection",
|
|
36
|
+
async ({ name, distance, enableHybrid }) => {
|
|
37
|
+
log.info(
|
|
38
|
+
{ tool: "create_collection", collection: name },
|
|
39
|
+
"Tool called",
|
|
40
|
+
);
|
|
41
|
+
const vectorSize = embeddings.getDimensions();
|
|
42
|
+
await qdrant.createCollection(
|
|
43
|
+
name,
|
|
44
|
+
vectorSize,
|
|
45
|
+
distance,
|
|
46
|
+
enableHybrid || false,
|
|
47
|
+
);
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
let message = `Collection "${name}" created successfully with ${vectorSize} dimensions and ${distance || "Cosine"} distance metric.`;
|
|
50
|
+
if (enableHybrid) {
|
|
51
|
+
message += " Hybrid search is enabled for this collection.";
|
|
52
|
+
}
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: message }],
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
),
|
|
48
59
|
);
|
|
49
60
|
|
|
50
61
|
// list_collections
|
|
@@ -55,12 +66,13 @@ export function registerCollectionTools(
|
|
|
55
66
|
description: "List all available collections in Qdrant.",
|
|
56
67
|
inputSchema: {},
|
|
57
68
|
},
|
|
58
|
-
async () => {
|
|
69
|
+
withToolLogging("list_collections", async () => {
|
|
70
|
+
log.info({ tool: "list_collections" }, "Tool called");
|
|
59
71
|
const collections = await qdrant.listCollections();
|
|
60
72
|
return {
|
|
61
73
|
content: [{ type: "text", text: JSON.stringify(collections, null, 2) }],
|
|
62
74
|
};
|
|
63
|
-
},
|
|
75
|
+
}),
|
|
64
76
|
);
|
|
65
77
|
|
|
66
78
|
// get_collection_info
|
|
@@ -72,12 +84,16 @@ export function registerCollectionTools(
|
|
|
72
84
|
"Get detailed information about a collection including vector size, point count, and distance metric.",
|
|
73
85
|
inputSchema: schemas.GetCollectionInfoSchema,
|
|
74
86
|
},
|
|
75
|
-
async ({ name }) => {
|
|
87
|
+
withToolLogging("get_collection_info", async ({ name }) => {
|
|
88
|
+
log.info(
|
|
89
|
+
{ tool: "get_collection_info", collection: name },
|
|
90
|
+
"Tool called",
|
|
91
|
+
);
|
|
76
92
|
const info = await qdrant.getCollectionInfo(name);
|
|
77
93
|
return {
|
|
78
94
|
content: [{ type: "text", text: JSON.stringify(info, null, 2) }],
|
|
79
95
|
};
|
|
80
|
-
},
|
|
96
|
+
}),
|
|
81
97
|
);
|
|
82
98
|
|
|
83
99
|
// delete_collection
|
|
@@ -88,13 +104,14 @@ export function registerCollectionTools(
|
|
|
88
104
|
description: "Delete a collection and all its documents.",
|
|
89
105
|
inputSchema: schemas.DeleteCollectionSchema,
|
|
90
106
|
},
|
|
91
|
-
async ({ name }) => {
|
|
107
|
+
withToolLogging("delete_collection", async ({ name }) => {
|
|
108
|
+
log.info({ tool: "delete_collection", collection: name }, "Tool called");
|
|
92
109
|
await qdrant.deleteCollection(name);
|
|
93
110
|
return {
|
|
94
111
|
content: [
|
|
95
112
|
{ type: "text", text: `Collection "${name}" deleted successfully.` },
|
|
96
113
|
],
|
|
97
114
|
};
|
|
98
|
-
},
|
|
115
|
+
}),
|
|
99
116
|
);
|
|
100
117
|
}
|
package/src/tools/document.ts
CHANGED
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import logger from "../logger.js";
|
|
6
7
|
import type { EmbeddingProvider } from "../embeddings/base.js";
|
|
7
8
|
import { BM25SparseVectorGenerator } from "../embeddings/sparse.js";
|
|
8
9
|
import type { QdrantManager } from "../qdrant/client.js";
|
|
10
|
+
import { withToolLogging } from "./logging.js";
|
|
9
11
|
import * as schemas from "./schemas.js";
|
|
10
12
|
|
|
13
|
+
const log = logger.child({ component: "tools" });
|
|
14
|
+
|
|
11
15
|
export interface DocumentToolDependencies {
|
|
12
16
|
qdrant: QdrantManager;
|
|
13
17
|
embeddings: EmbeddingProvider;
|
|
@@ -28,7 +32,11 @@ export function registerDocumentTools(
|
|
|
28
32
|
"Add documents to a collection. Documents will be automatically embedded using the configured embedding provider.",
|
|
29
33
|
inputSchema: schemas.AddDocumentsSchema,
|
|
30
34
|
},
|
|
31
|
-
async ({ collection, documents }) => {
|
|
35
|
+
withToolLogging("add_documents", async ({ collection, documents }) => {
|
|
36
|
+
log.info(
|
|
37
|
+
{ tool: "add_documents", collection, count: documents.length },
|
|
38
|
+
"Tool called",
|
|
39
|
+
);
|
|
32
40
|
// Check if collection exists and get info
|
|
33
41
|
const exists = await qdrant.collectionExists(collection);
|
|
34
42
|
if (!exists) {
|
|
@@ -46,7 +54,7 @@ export function registerDocumentTools(
|
|
|
46
54
|
const collectionInfo = await qdrant.getCollectionInfo(collection);
|
|
47
55
|
|
|
48
56
|
// Generate embeddings for all documents
|
|
49
|
-
const texts = documents.map((doc) => doc.text);
|
|
57
|
+
const texts = documents.map((doc: { text: string }) => doc.text);
|
|
50
58
|
const embeddingResults = await embeddings.embedBatch(texts);
|
|
51
59
|
|
|
52
60
|
// If hybrid search is enabled, generate sparse vectors and use appropriate method
|
|
@@ -54,27 +62,45 @@ export function registerDocumentTools(
|
|
|
54
62
|
const sparseGenerator = new BM25SparseVectorGenerator();
|
|
55
63
|
|
|
56
64
|
// Prepare points with both dense and sparse vectors
|
|
57
|
-
const points = documents.map(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const points = documents.map(
|
|
66
|
+
(
|
|
67
|
+
doc: {
|
|
68
|
+
id: string | number;
|
|
69
|
+
text: string;
|
|
70
|
+
metadata?: Record<string, any>;
|
|
71
|
+
},
|
|
72
|
+
index: number,
|
|
73
|
+
) => ({
|
|
74
|
+
id: doc.id,
|
|
75
|
+
vector: embeddingResults[index].embedding,
|
|
76
|
+
sparseVector: sparseGenerator.generate(doc.text),
|
|
77
|
+
payload: {
|
|
78
|
+
text: doc.text,
|
|
79
|
+
...doc.metadata,
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
66
83
|
|
|
67
84
|
await qdrant.addPointsWithSparse(collection, points);
|
|
68
85
|
} else {
|
|
69
86
|
// Standard dense-only vectors
|
|
70
|
-
const points = documents.map(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
const points = documents.map(
|
|
88
|
+
(
|
|
89
|
+
doc: {
|
|
90
|
+
id: string | number;
|
|
91
|
+
text: string;
|
|
92
|
+
metadata?: Record<string, any>;
|
|
93
|
+
},
|
|
94
|
+
index: number,
|
|
95
|
+
) => ({
|
|
96
|
+
id: doc.id,
|
|
97
|
+
vector: embeddingResults[index].embedding,
|
|
98
|
+
payload: {
|
|
99
|
+
text: doc.text,
|
|
100
|
+
...doc.metadata,
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
78
104
|
|
|
79
105
|
await qdrant.addPoints(collection, points);
|
|
80
106
|
}
|
|
@@ -87,7 +113,7 @@ export function registerDocumentTools(
|
|
|
87
113
|
},
|
|
88
114
|
],
|
|
89
115
|
};
|
|
90
|
-
},
|
|
116
|
+
}),
|
|
91
117
|
);
|
|
92
118
|
|
|
93
119
|
// delete_documents
|
|
@@ -98,7 +124,11 @@ export function registerDocumentTools(
|
|
|
98
124
|
description: "Delete specific documents from a collection by their IDs.",
|
|
99
125
|
inputSchema: schemas.DeleteDocumentsSchema,
|
|
100
126
|
},
|
|
101
|
-
async ({ collection, ids }) => {
|
|
127
|
+
withToolLogging("delete_documents", async ({ collection, ids }) => {
|
|
128
|
+
log.info(
|
|
129
|
+
{ tool: "delete_documents", collection, count: ids.length },
|
|
130
|
+
"Tool called",
|
|
131
|
+
);
|
|
102
132
|
await qdrant.deletePoints(collection, ids);
|
|
103
133
|
return {
|
|
104
134
|
content: [
|
|
@@ -108,6 +138,6 @@ export function registerDocumentTools(
|
|
|
108
138
|
},
|
|
109
139
|
],
|
|
110
140
|
};
|
|
111
|
-
},
|
|
141
|
+
}),
|
|
112
142
|
);
|
|
113
143
|
}
|
|
@@ -8,6 +8,18 @@ import {
|
|
|
8
8
|
pathsMatch,
|
|
9
9
|
} from "./federated.js";
|
|
10
10
|
|
|
11
|
+
vi.mock("../logger.js", () => ({
|
|
12
|
+
default: {
|
|
13
|
+
info: vi.fn(),
|
|
14
|
+
warn: vi.fn(),
|
|
15
|
+
error: vi.fn(),
|
|
16
|
+
debug: vi.fn(),
|
|
17
|
+
fatal: vi.fn(),
|
|
18
|
+
trace: vi.fn(),
|
|
19
|
+
child: vi.fn().mockReturnThis(),
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
|
|
11
23
|
// ============================================================================
|
|
12
24
|
// Unit Tests for Helper Functions
|
|
13
25
|
// ============================================================================
|
package/src/tools/federated.ts
CHANGED
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import logger from "../logger.js";
|
|
10
11
|
import type { CodeIndexer } from "../code/indexer.js";
|
|
11
12
|
import type { CodeSearchResult } from "../code/types.js";
|
|
12
13
|
import type { GitHistoryIndexer } from "../git/indexer.js";
|
|
13
14
|
import type { GitSearchResult } from "../git/types.js";
|
|
15
|
+
import { withToolLogging } from "./logging.js";
|
|
14
16
|
import * as schemas from "./schemas.js";
|
|
15
17
|
|
|
18
|
+
const log = logger.child({ component: "tools" });
|
|
19
|
+
|
|
16
20
|
// ============================================================================
|
|
17
21
|
// Types
|
|
18
22
|
// ============================================================================
|
|
@@ -412,81 +416,88 @@ export function registerFederatedTools(
|
|
|
412
416
|
"modified which files. Useful for understanding code evolution and finding related changes.",
|
|
413
417
|
inputSchema: schemas.ContextualSearchSchema,
|
|
414
418
|
},
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
419
|
+
withToolLogging(
|
|
420
|
+
"contextual_search",
|
|
421
|
+
async ({ path, query, codeLimit, gitLimit, correlate }) => {
|
|
422
|
+
log.info(
|
|
423
|
+
{ tool: "contextual_search", path, query: query.substring(0, 80) },
|
|
424
|
+
"Tool called",
|
|
421
425
|
);
|
|
426
|
+
try {
|
|
427
|
+
const result = await performContextualSearch(
|
|
428
|
+
codeIndexer,
|
|
429
|
+
gitHistoryIndexer,
|
|
430
|
+
{ path, query, codeLimit, gitLimit, correlate },
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// Format output
|
|
434
|
+
const sections: string[] = [];
|
|
435
|
+
|
|
436
|
+
// Code results section
|
|
437
|
+
if (result.codeResults.length > 0) {
|
|
438
|
+
sections.push("## Code Results\n");
|
|
439
|
+
result.codeResults.forEach((r, idx) => {
|
|
440
|
+
sections.push(
|
|
441
|
+
`### ${idx + 1}. ${r.filePath}:${r.startLine}-${r.endLine} (score: ${r.score.toFixed(3)})\n` +
|
|
442
|
+
`Language: ${r.language}\n` +
|
|
443
|
+
"```" +
|
|
444
|
+
r.language +
|
|
445
|
+
"\n" +
|
|
446
|
+
r.content +
|
|
447
|
+
"\n```\n",
|
|
448
|
+
);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
422
451
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
r.language +
|
|
435
|
-
"\n" +
|
|
436
|
-
r.content +
|
|
437
|
-
"\n```\n",
|
|
438
|
-
);
|
|
439
|
-
});
|
|
440
|
-
}
|
|
452
|
+
// Git results section
|
|
453
|
+
if (result.gitResults.length > 0) {
|
|
454
|
+
sections.push("\n## Git History Results\n");
|
|
455
|
+
result.gitResults.forEach((r, idx) => {
|
|
456
|
+
sections.push(
|
|
457
|
+
`### ${idx + 1}. ${r.shortHash} - ${r.subject} (score: ${r.score.toFixed(3)})\n` +
|
|
458
|
+
`Author: ${r.author} | Date: ${r.date} | Type: ${r.commitType}\n` +
|
|
459
|
+
`Files: ${r.files.slice(0, 5).join(", ")}${r.files.length > 5 ? ` (+${r.files.length - 5} more)` : ""}\n`,
|
|
460
|
+
);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
441
463
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
464
|
+
// Correlations section
|
|
465
|
+
if (result.correlations.length > 0) {
|
|
466
|
+
sections.push("\n## Correlations (Code ↔ Commits)\n");
|
|
467
|
+
result.correlations.forEach((c) => {
|
|
468
|
+
const commits = c.relatedCommits
|
|
469
|
+
.slice(0, 3)
|
|
470
|
+
.map((commit) => ` - ${commit.shortHash}: ${commit.subject}`)
|
|
471
|
+
.join("\n");
|
|
472
|
+
sections.push(
|
|
473
|
+
`**${c.codeResult.filePath}:${c.codeResult.startLine}** modified by:\n${commits}\n`,
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
453
477
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
.map((commit) => ` - ${commit.shortHash}: ${commit.subject}`)
|
|
461
|
-
.join("\n");
|
|
462
|
-
sections.push(
|
|
463
|
-
`**${c.codeResult.filePath}:${c.codeResult.startLine}** modified by:\n${commits}\n`,
|
|
464
|
-
);
|
|
465
|
-
});
|
|
466
|
-
}
|
|
478
|
+
// Summary
|
|
479
|
+
const summary =
|
|
480
|
+
`\n---\nFound ${result.metadata.codeResultCount} code result(s), ` +
|
|
481
|
+
`${result.metadata.gitResultCount} git result(s), ` +
|
|
482
|
+
`${result.metadata.correlationCount} correlation(s).`;
|
|
483
|
+
sections.push(summary);
|
|
467
484
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
},
|
|
485
|
-
],
|
|
486
|
-
isError: true,
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
},
|
|
485
|
+
return {
|
|
486
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
487
|
+
};
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return {
|
|
490
|
+
content: [
|
|
491
|
+
{
|
|
492
|
+
type: "text",
|
|
493
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
isError: true,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
),
|
|
490
501
|
);
|
|
491
502
|
|
|
492
503
|
// federated_search
|
|
@@ -500,70 +511,77 @@ export function registerFederatedTools(
|
|
|
500
511
|
"Supports code-only, git-only, or combined search modes.",
|
|
501
512
|
inputSchema: schemas.FederatedSearchSchema,
|
|
502
513
|
},
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
514
|
+
withToolLogging(
|
|
515
|
+
"federated_search",
|
|
516
|
+
async ({ paths, query, searchType, limit }) => {
|
|
517
|
+
log.info(
|
|
518
|
+
{ tool: "federated_search", paths, query: query.substring(0, 80) },
|
|
519
|
+
"Tool called",
|
|
509
520
|
);
|
|
521
|
+
try {
|
|
522
|
+
const response = await performFederatedSearch(
|
|
523
|
+
codeIndexer,
|
|
524
|
+
gitHistoryIndexer,
|
|
525
|
+
{ paths, query, searchType, limit },
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
if (response.results.length === 0) {
|
|
529
|
+
return {
|
|
530
|
+
content: [
|
|
531
|
+
{
|
|
532
|
+
type: "text",
|
|
533
|
+
text: `No results found for query "${query}" across ${paths.length} repository(ies).`,
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
};
|
|
537
|
+
}
|
|
510
538
|
|
|
511
|
-
|
|
539
|
+
// Format results
|
|
540
|
+
const sections: string[] = [
|
|
541
|
+
`# Federated Search Results\n` +
|
|
542
|
+
`Query: "${query}" | Type: ${response.metadata.searchType} | ` +
|
|
543
|
+
`Repositories: ${response.metadata.repositoriesSearched.length}\n`,
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
response.results.forEach((r, idx) => {
|
|
547
|
+
if (r.resultType === "code") {
|
|
548
|
+
sections.push(
|
|
549
|
+
`## ${idx + 1}. [CODE] ${r.filePath}:${r.startLine}-${r.endLine}\n` +
|
|
550
|
+
`Repository: ${r.repoPath} | Language: ${r.language} | Score: ${r.score.toFixed(3)}\n` +
|
|
551
|
+
"```" +
|
|
552
|
+
r.language +
|
|
553
|
+
"\n" +
|
|
554
|
+
r.content +
|
|
555
|
+
"\n```\n",
|
|
556
|
+
);
|
|
557
|
+
} else {
|
|
558
|
+
sections.push(
|
|
559
|
+
`## ${idx + 1}. [GIT] ${r.shortHash} - ${r.subject}\n` +
|
|
560
|
+
`Repository: ${r.repoPath} | Author: ${r.author} | Date: ${r.date} | Score: ${r.score.toFixed(3)}\n` +
|
|
561
|
+
`Type: ${r.commitType} | Files: ${r.files.slice(0, 3).join(", ")}${r.files.length > 3 ? ` (+${r.files.length - 3} more)` : ""}\n`,
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
sections.push(
|
|
567
|
+
`\n---\nTotal: ${response.metadata.totalResults} result(s) from ${response.metadata.repositoriesSearched.length} repository(ies).`,
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
return {
|
|
571
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
572
|
+
};
|
|
573
|
+
} catch (error) {
|
|
512
574
|
return {
|
|
513
575
|
content: [
|
|
514
576
|
{
|
|
515
577
|
type: "text",
|
|
516
|
-
text: `
|
|
578
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
517
579
|
},
|
|
518
580
|
],
|
|
581
|
+
isError: true,
|
|
519
582
|
};
|
|
520
583
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const sections: string[] = [
|
|
524
|
-
`# Federated Search Results\n` +
|
|
525
|
-
`Query: "${query}" | Type: ${response.metadata.searchType} | ` +
|
|
526
|
-
`Repositories: ${response.metadata.repositoriesSearched.length}\n`,
|
|
527
|
-
];
|
|
528
|
-
|
|
529
|
-
response.results.forEach((r, idx) => {
|
|
530
|
-
if (r.resultType === "code") {
|
|
531
|
-
sections.push(
|
|
532
|
-
`## ${idx + 1}. [CODE] ${r.filePath}:${r.startLine}-${r.endLine}\n` +
|
|
533
|
-
`Repository: ${r.repoPath} | Language: ${r.language} | Score: ${r.score.toFixed(3)}\n` +
|
|
534
|
-
"```" +
|
|
535
|
-
r.language +
|
|
536
|
-
"\n" +
|
|
537
|
-
r.content +
|
|
538
|
-
"\n```\n",
|
|
539
|
-
);
|
|
540
|
-
} else {
|
|
541
|
-
sections.push(
|
|
542
|
-
`## ${idx + 1}. [GIT] ${r.shortHash} - ${r.subject}\n` +
|
|
543
|
-
`Repository: ${r.repoPath} | Author: ${r.author} | Date: ${r.date} | Score: ${r.score.toFixed(3)}\n` +
|
|
544
|
-
`Type: ${r.commitType} | Files: ${r.files.slice(0, 3).join(", ")}${r.files.length > 3 ? ` (+${r.files.length - 3} more)` : ""}\n`,
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
sections.push(
|
|
550
|
-
`\n---\nTotal: ${response.metadata.totalResults} result(s) from ${response.metadata.repositoriesSearched.length} repository(ies).`,
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
return {
|
|
554
|
-
content: [{ type: "text", text: sections.join("\n") }],
|
|
555
|
-
};
|
|
556
|
-
} catch (error) {
|
|
557
|
-
return {
|
|
558
|
-
content: [
|
|
559
|
-
{
|
|
560
|
-
type: "text",
|
|
561
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
562
|
-
},
|
|
563
|
-
],
|
|
564
|
-
isError: true,
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
},
|
|
584
|
+
},
|
|
585
|
+
),
|
|
568
586
|
);
|
|
569
587
|
}
|