@mhalder/qdrant-mcp-server 1.4.0 → 1.6.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/.codecov.yml +16 -0
- package/.github/workflows/claude-code-review.yml +6 -5
- package/.releaserc.json +8 -1
- package/CHANGELOG.md +34 -0
- package/README.md +259 -9
- package/build/code/chunker/base.d.ts +19 -0
- package/build/code/chunker/base.d.ts.map +1 -0
- package/build/code/chunker/base.js +5 -0
- package/build/code/chunker/base.js.map +1 -0
- package/build/code/chunker/character-chunker.d.ts +22 -0
- package/build/code/chunker/character-chunker.d.ts.map +1 -0
- package/build/code/chunker/character-chunker.js +111 -0
- package/build/code/chunker/character-chunker.js.map +1 -0
- package/build/code/chunker/tree-sitter-chunker.d.ts +29 -0
- package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -0
- package/build/code/chunker/tree-sitter-chunker.js +213 -0
- package/build/code/chunker/tree-sitter-chunker.js.map +1 -0
- package/build/code/config.d.ts +11 -0
- package/build/code/config.d.ts.map +1 -0
- package/build/code/config.js +145 -0
- package/build/code/config.js.map +1 -0
- package/build/code/indexer.d.ts +42 -0
- package/build/code/indexer.d.ts.map +1 -0
- package/build/code/indexer.js +508 -0
- package/build/code/indexer.js.map +1 -0
- package/build/code/metadata.d.ts +32 -0
- package/build/code/metadata.d.ts.map +1 -0
- package/build/code/metadata.js +128 -0
- package/build/code/metadata.js.map +1 -0
- package/build/code/scanner.d.ts +35 -0
- package/build/code/scanner.d.ts.map +1 -0
- package/build/code/scanner.js +108 -0
- package/build/code/scanner.js.map +1 -0
- package/build/code/sync/merkle.d.ts +45 -0
- package/build/code/sync/merkle.d.ts.map +1 -0
- package/build/code/sync/merkle.js +116 -0
- package/build/code/sync/merkle.js.map +1 -0
- package/build/code/sync/snapshot.d.ts +41 -0
- package/build/code/sync/snapshot.d.ts.map +1 -0
- package/build/code/sync/snapshot.js +91 -0
- package/build/code/sync/snapshot.js.map +1 -0
- package/build/code/sync/synchronizer.d.ts +53 -0
- package/build/code/sync/synchronizer.d.ts.map +1 -0
- package/build/code/sync/synchronizer.js +132 -0
- package/build/code/sync/synchronizer.js.map +1 -0
- package/build/code/types.d.ts +98 -0
- package/build/code/types.d.ts.map +1 -0
- package/build/code/types.js +5 -0
- package/build/code/types.js.map +1 -0
- package/build/index.js +252 -1
- package/build/index.js.map +1 -1
- package/build/qdrant/client.d.ts +1 -1
- package/build/qdrant/client.d.ts.map +1 -1
- package/build/qdrant/client.js +2 -2
- package/build/qdrant/client.js.map +1 -1
- package/build/qdrant/client.test.js +16 -0
- package/build/qdrant/client.test.js.map +1 -1
- package/examples/code-search/README.md +271 -0
- package/package.json +15 -2
- package/src/code/chunker/base.ts +22 -0
- package/src/code/chunker/character-chunker.ts +131 -0
- package/src/code/chunker/tree-sitter-chunker.ts +250 -0
- package/src/code/config.ts +156 -0
- package/src/code/indexer.ts +613 -0
- package/src/code/metadata.ts +153 -0
- package/src/code/scanner.ts +124 -0
- package/src/code/sync/merkle.ts +136 -0
- package/src/code/sync/snapshot.ts +110 -0
- package/src/code/sync/synchronizer.ts +154 -0
- package/src/code/types.ts +117 -0
- package/src/index.ts +298 -1
- package/src/qdrant/client.test.ts +20 -0
- package/src/qdrant/client.ts +2 -2
- package/tests/code/chunker/character-chunker.test.ts +141 -0
- package/tests/code/chunker/tree-sitter-chunker.test.ts +275 -0
- package/tests/code/fixtures/sample-py/calculator.py +32 -0
- package/tests/code/fixtures/sample-ts/async-operations.ts +120 -0
- package/tests/code/fixtures/sample-ts/auth.ts +31 -0
- package/tests/code/fixtures/sample-ts/config.ts +52 -0
- package/tests/code/fixtures/sample-ts/database.ts +50 -0
- package/tests/code/fixtures/sample-ts/index.ts +39 -0
- package/tests/code/fixtures/sample-ts/types-advanced.ts +132 -0
- package/tests/code/fixtures/sample-ts/utils.ts +105 -0
- package/tests/code/fixtures/sample-ts/validator.ts +169 -0
- package/tests/code/indexer.test.ts +828 -0
- package/tests/code/integration.test.ts +708 -0
- package/tests/code/metadata.test.ts +457 -0
- package/tests/code/scanner.test.ts +131 -0
- package/tests/code/sync/merkle.test.ts +406 -0
- package/tests/code/sync/snapshot.test.ts +360 -0
- package/tests/code/sync/synchronizer.test.ts +501 -0
- package/vitest.config.ts +1 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileSynchronizer - Manages incremental updates using Merkle trees
|
|
3
|
+
* Detects file changes and updates snapshots
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { join, relative } from "node:path";
|
|
9
|
+
import { MerkleTree } from "./merkle.js";
|
|
10
|
+
import { SnapshotManager } from "./snapshot.js";
|
|
11
|
+
export class FileSynchronizer {
|
|
12
|
+
codebasePath;
|
|
13
|
+
snapshotManager;
|
|
14
|
+
previousHashes = new Map();
|
|
15
|
+
previousTree = null;
|
|
16
|
+
constructor(codebasePath, collectionName) {
|
|
17
|
+
this.codebasePath = codebasePath;
|
|
18
|
+
// Store snapshots in ~/.qdrant-mcp/snapshots/
|
|
19
|
+
const snapshotDir = join(homedir(), ".qdrant-mcp", "snapshots");
|
|
20
|
+
const snapshotPath = join(snapshotDir, `${collectionName}.json`);
|
|
21
|
+
this.snapshotManager = new SnapshotManager(snapshotPath);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Initialize synchronizer by loading previous snapshot
|
|
25
|
+
*/
|
|
26
|
+
async initialize() {
|
|
27
|
+
const snapshot = await this.snapshotManager.load();
|
|
28
|
+
if (snapshot) {
|
|
29
|
+
this.previousHashes = snapshot.fileHashes;
|
|
30
|
+
this.previousTree = snapshot.merkleTree;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Compute hash for a file's content
|
|
37
|
+
*/
|
|
38
|
+
async hashFile(filePath) {
|
|
39
|
+
try {
|
|
40
|
+
// Resolve path relative to codebase if not absolute
|
|
41
|
+
const absolutePath = filePath.startsWith(this.codebasePath)
|
|
42
|
+
? filePath
|
|
43
|
+
: join(this.codebasePath, filePath);
|
|
44
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
45
|
+
return createHash("sha256").update(content).digest("hex");
|
|
46
|
+
}
|
|
47
|
+
catch (_error) {
|
|
48
|
+
// If file can't be read, return empty hash
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compute hashes for all files
|
|
54
|
+
*/
|
|
55
|
+
async computeFileHashes(filePaths) {
|
|
56
|
+
const fileHashes = new Map();
|
|
57
|
+
for (const filePath of filePaths) {
|
|
58
|
+
const hash = await this.hashFile(filePath);
|
|
59
|
+
if (hash) {
|
|
60
|
+
// Normalize to relative path
|
|
61
|
+
const relativePath = filePath.startsWith(this.codebasePath)
|
|
62
|
+
? relative(this.codebasePath, filePath)
|
|
63
|
+
: filePath;
|
|
64
|
+
fileHashes.set(relativePath, hash);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return fileHashes;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Detect changes since last snapshot
|
|
71
|
+
*/
|
|
72
|
+
async detectChanges(currentFiles) {
|
|
73
|
+
// Compute current hashes
|
|
74
|
+
const currentHashes = await this.computeFileHashes(currentFiles);
|
|
75
|
+
// Compare with previous snapshot
|
|
76
|
+
const changes = MerkleTree.compare(this.previousHashes, currentHashes);
|
|
77
|
+
return changes;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Update snapshot with current state
|
|
81
|
+
*/
|
|
82
|
+
async updateSnapshot(files) {
|
|
83
|
+
const fileHashes = await this.computeFileHashes(files);
|
|
84
|
+
const tree = new MerkleTree();
|
|
85
|
+
tree.build(fileHashes);
|
|
86
|
+
await this.snapshotManager.save(this.codebasePath, fileHashes, tree);
|
|
87
|
+
// Update internal state
|
|
88
|
+
this.previousHashes = fileHashes;
|
|
89
|
+
this.previousTree = tree;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Delete snapshot
|
|
93
|
+
*/
|
|
94
|
+
async deleteSnapshot() {
|
|
95
|
+
await this.snapshotManager.delete();
|
|
96
|
+
this.previousHashes.clear();
|
|
97
|
+
this.previousTree = null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if snapshot exists
|
|
101
|
+
*/
|
|
102
|
+
async hasSnapshot() {
|
|
103
|
+
return this.snapshotManager.exists();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validate snapshot integrity
|
|
107
|
+
*/
|
|
108
|
+
async validateSnapshot() {
|
|
109
|
+
return this.snapshotManager.validate();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get snapshot age in milliseconds
|
|
113
|
+
*/
|
|
114
|
+
async getSnapshotAge() {
|
|
115
|
+
const snapshot = await this.snapshotManager.load();
|
|
116
|
+
if (!snapshot)
|
|
117
|
+
return null;
|
|
118
|
+
return Date.now() - snapshot.timestamp;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Quick check if re-indexing is needed (compare root hashes)
|
|
122
|
+
*/
|
|
123
|
+
async needsReindex(currentFiles) {
|
|
124
|
+
if (!this.previousTree)
|
|
125
|
+
return true;
|
|
126
|
+
const currentHashes = await this.computeFileHashes(currentFiles);
|
|
127
|
+
const currentTree = new MerkleTree();
|
|
128
|
+
currentTree.build(currentHashes);
|
|
129
|
+
return this.previousTree.getRootHash() !== currentTree.getRootHash();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=synchronizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synchronizer.js","sourceRoot":"","sources":["../../../src/code/sync/synchronizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for code vectorization module
|
|
3
|
+
*/
|
|
4
|
+
export interface CodeConfig {
|
|
5
|
+
chunkSize: number;
|
|
6
|
+
chunkOverlap: number;
|
|
7
|
+
enableASTChunking: boolean;
|
|
8
|
+
supportedExtensions: string[];
|
|
9
|
+
ignorePatterns: string[];
|
|
10
|
+
customExtensions?: string[];
|
|
11
|
+
customIgnorePatterns?: string[];
|
|
12
|
+
batchSize: number;
|
|
13
|
+
maxChunksPerFile?: number;
|
|
14
|
+
maxTotalChunks?: number;
|
|
15
|
+
defaultSearchLimit: number;
|
|
16
|
+
enableHybridSearch: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface ScannerConfig {
|
|
19
|
+
supportedExtensions: string[];
|
|
20
|
+
ignorePatterns: string[];
|
|
21
|
+
customIgnorePatterns?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface ChunkerConfig {
|
|
24
|
+
chunkSize: number;
|
|
25
|
+
chunkOverlap: number;
|
|
26
|
+
maxChunkSize: number;
|
|
27
|
+
}
|
|
28
|
+
export interface IndexOptions {
|
|
29
|
+
forceReindex?: boolean;
|
|
30
|
+
extensions?: string[];
|
|
31
|
+
ignorePatterns?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface IndexStats {
|
|
34
|
+
filesScanned: number;
|
|
35
|
+
filesIndexed: number;
|
|
36
|
+
chunksCreated: number;
|
|
37
|
+
durationMs: number;
|
|
38
|
+
status: "completed" | "partial" | "failed";
|
|
39
|
+
errors?: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface ChangeStats {
|
|
42
|
+
filesAdded: number;
|
|
43
|
+
filesModified: number;
|
|
44
|
+
filesDeleted: number;
|
|
45
|
+
chunksAdded: number;
|
|
46
|
+
chunksDeleted: number;
|
|
47
|
+
durationMs: number;
|
|
48
|
+
}
|
|
49
|
+
export interface CodeSearchResult {
|
|
50
|
+
content: string;
|
|
51
|
+
filePath: string;
|
|
52
|
+
startLine: number;
|
|
53
|
+
endLine: number;
|
|
54
|
+
language: string;
|
|
55
|
+
score: number;
|
|
56
|
+
fileExtension: string;
|
|
57
|
+
}
|
|
58
|
+
export interface SearchOptions {
|
|
59
|
+
limit?: number;
|
|
60
|
+
useHybrid?: boolean;
|
|
61
|
+
fileTypes?: string[];
|
|
62
|
+
pathPattern?: string;
|
|
63
|
+
scoreThreshold?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface IndexStatus {
|
|
66
|
+
isIndexed: boolean;
|
|
67
|
+
collectionName?: string;
|
|
68
|
+
filesCount?: number;
|
|
69
|
+
chunksCount?: number;
|
|
70
|
+
lastUpdated?: Date;
|
|
71
|
+
languages?: string[];
|
|
72
|
+
}
|
|
73
|
+
export type ProgressCallback = (progress: ProgressUpdate) => void;
|
|
74
|
+
export interface ProgressUpdate {
|
|
75
|
+
phase: "scanning" | "chunking" | "embedding" | "storing";
|
|
76
|
+
current: number;
|
|
77
|
+
total: number;
|
|
78
|
+
percentage: number;
|
|
79
|
+
message: string;
|
|
80
|
+
}
|
|
81
|
+
export interface CodeChunk {
|
|
82
|
+
content: string;
|
|
83
|
+
startLine: number;
|
|
84
|
+
endLine: number;
|
|
85
|
+
metadata: {
|
|
86
|
+
filePath: string;
|
|
87
|
+
language: string;
|
|
88
|
+
chunkIndex: number;
|
|
89
|
+
chunkType?: "function" | "class" | "interface" | "block";
|
|
90
|
+
name?: string;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export interface FileChanges {
|
|
94
|
+
added: string[];
|
|
95
|
+
modified: string[];
|
|
96
|
+
deleted: string[];
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/code/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/code/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/build/index.js
CHANGED
|
@@ -9,6 +9,8 @@ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema
|
|
|
9
9
|
import Bottleneck from "bottleneck";
|
|
10
10
|
import express from "express";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
+
import { DEFAULT_BATCH_SIZE, DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE, DEFAULT_CODE_EXTENSIONS, DEFAULT_IGNORE_PATTERNS, DEFAULT_SEARCH_LIMIT, } from "./code/config.js";
|
|
13
|
+
import { CodeIndexer } from "./code/indexer.js";
|
|
12
14
|
import { EmbeddingProviderFactory } from "./embeddings/factory.js";
|
|
13
15
|
import { BM25SparseVectorGenerator } from "./embeddings/sparse.js";
|
|
14
16
|
import { getPrompt, listPrompts, loadPromptsConfig } from "./prompts/index.js";
|
|
@@ -19,6 +21,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
19
21
|
const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
20
22
|
// Validate environment variables
|
|
21
23
|
const QDRANT_URL = process.env.QDRANT_URL || "http://localhost:6333";
|
|
24
|
+
const QDRANT_API_KEY = process.env.QDRANT_API_KEY;
|
|
22
25
|
const EMBEDDING_PROVIDER = (process.env.EMBEDDING_PROVIDER || "ollama").toLowerCase();
|
|
23
26
|
const TRANSPORT_MODE = (process.env.TRANSPORT_MODE || "stdio").toLowerCase();
|
|
24
27
|
const HTTP_PORT = parseInt(process.env.HTTP_PORT || "3000", 10);
|
|
@@ -114,8 +117,20 @@ async function checkOllamaAvailability() {
|
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
// Initialize clients
|
|
117
|
-
const qdrant = new QdrantManager(QDRANT_URL);
|
|
120
|
+
const qdrant = new QdrantManager(QDRANT_URL, QDRANT_API_KEY);
|
|
118
121
|
const embeddings = EmbeddingProviderFactory.createFromEnv();
|
|
122
|
+
// Initialize code indexer
|
|
123
|
+
const codeConfig = {
|
|
124
|
+
chunkSize: parseInt(process.env.CODE_CHUNK_SIZE || String(DEFAULT_CHUNK_SIZE), 10),
|
|
125
|
+
chunkOverlap: parseInt(process.env.CODE_CHUNK_OVERLAP || String(DEFAULT_CHUNK_OVERLAP), 10),
|
|
126
|
+
enableASTChunking: process.env.CODE_ENABLE_AST !== "false",
|
|
127
|
+
supportedExtensions: DEFAULT_CODE_EXTENSIONS,
|
|
128
|
+
ignorePatterns: DEFAULT_IGNORE_PATTERNS,
|
|
129
|
+
batchSize: parseInt(process.env.CODE_BATCH_SIZE || String(DEFAULT_BATCH_SIZE), 10),
|
|
130
|
+
defaultSearchLimit: parseInt(process.env.CODE_SEARCH_LIMIT || String(DEFAULT_SEARCH_LIMIT), 10),
|
|
131
|
+
enableHybridSearch: process.env.CODE_ENABLE_HYBRID === "true",
|
|
132
|
+
};
|
|
133
|
+
const codeIndexer = new CodeIndexer(qdrant, embeddings, codeConfig);
|
|
119
134
|
// Load prompts configuration if file exists
|
|
120
135
|
let promptsConfig = null;
|
|
121
136
|
if (existsSync(PROMPTS_CONFIG_FILE)) {
|
|
@@ -322,6 +337,107 @@ function registerHandlers(server) {
|
|
|
322
337
|
required: ["collection", "query"],
|
|
323
338
|
},
|
|
324
339
|
},
|
|
340
|
+
{
|
|
341
|
+
name: "index_codebase",
|
|
342
|
+
description: "Index a codebase for semantic code search. Automatically discovers files, chunks code intelligently using AST-aware parsing, and stores in vector database. Respects .gitignore and other ignore files.",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: "object",
|
|
345
|
+
properties: {
|
|
346
|
+
path: {
|
|
347
|
+
type: "string",
|
|
348
|
+
description: "Absolute or relative path to codebase root directory",
|
|
349
|
+
},
|
|
350
|
+
forceReindex: {
|
|
351
|
+
type: "boolean",
|
|
352
|
+
description: "Force full re-index even if already indexed (default: false)",
|
|
353
|
+
},
|
|
354
|
+
extensions: {
|
|
355
|
+
type: "array",
|
|
356
|
+
items: { type: "string" },
|
|
357
|
+
description: "Custom file extensions to index (e.g., ['.proto', '.graphql'])",
|
|
358
|
+
},
|
|
359
|
+
ignorePatterns: {
|
|
360
|
+
type: "array",
|
|
361
|
+
items: { type: "string" },
|
|
362
|
+
description: "Additional patterns to ignore (e.g., ['**/test/**', '**/*.test.ts'])",
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
required: ["path"],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: "search_code",
|
|
370
|
+
description: "Search indexed codebase using natural language queries. Returns semantically relevant code chunks with file paths and line numbers.",
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: "object",
|
|
373
|
+
properties: {
|
|
374
|
+
path: {
|
|
375
|
+
type: "string",
|
|
376
|
+
description: "Path to codebase (must be indexed first)",
|
|
377
|
+
},
|
|
378
|
+
query: {
|
|
379
|
+
type: "string",
|
|
380
|
+
description: "Natural language search query (e.g., 'authentication logic')",
|
|
381
|
+
},
|
|
382
|
+
limit: {
|
|
383
|
+
type: "number",
|
|
384
|
+
description: "Maximum number of results (default: 5, max: 100)",
|
|
385
|
+
},
|
|
386
|
+
fileTypes: {
|
|
387
|
+
type: "array",
|
|
388
|
+
items: { type: "string" },
|
|
389
|
+
description: "Filter by file extensions (e.g., ['.ts', '.py'])",
|
|
390
|
+
},
|
|
391
|
+
pathPattern: {
|
|
392
|
+
type: "string",
|
|
393
|
+
description: "Filter by path glob pattern (e.g., 'src/services/**')",
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
required: ["path", "query"],
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "reindex_changes",
|
|
401
|
+
description: "Incrementally re-index only changed files. Detects added, modified, and deleted files since last index. Requires previous indexing with index_codebase.",
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties: {
|
|
405
|
+
path: {
|
|
406
|
+
type: "string",
|
|
407
|
+
description: "Path to codebase",
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
required: ["path"],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: "get_index_status",
|
|
415
|
+
description: "Get indexing status and statistics for a codebase.",
|
|
416
|
+
inputSchema: {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
path: {
|
|
420
|
+
type: "string",
|
|
421
|
+
description: "Path to codebase",
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
required: ["path"],
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
name: "clear_index",
|
|
429
|
+
description: "Delete all indexed data for a codebase. This is irreversible and will remove the entire collection.",
|
|
430
|
+
inputSchema: {
|
|
431
|
+
type: "object",
|
|
432
|
+
properties: {
|
|
433
|
+
path: {
|
|
434
|
+
type: "string",
|
|
435
|
+
description: "Path to codebase",
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
required: ["path"],
|
|
439
|
+
},
|
|
440
|
+
},
|
|
325
441
|
],
|
|
326
442
|
};
|
|
327
443
|
});
|
|
@@ -521,6 +637,141 @@ function registerHandlers(server) {
|
|
|
521
637
|
],
|
|
522
638
|
};
|
|
523
639
|
}
|
|
640
|
+
case "index_codebase": {
|
|
641
|
+
const IndexCodebaseSchema = z.object({
|
|
642
|
+
path: z.string(),
|
|
643
|
+
forceReindex: z.boolean().optional(),
|
|
644
|
+
extensions: z.array(z.string()).optional(),
|
|
645
|
+
ignorePatterns: z.array(z.string()).optional(),
|
|
646
|
+
});
|
|
647
|
+
const { path, forceReindex, extensions, ignorePatterns } = IndexCodebaseSchema.parse(args);
|
|
648
|
+
const stats = await codeIndexer.indexCodebase(path, { forceReindex, extensions, ignorePatterns }, (progress) => {
|
|
649
|
+
// Progress callback - could send progress updates via SSE in future
|
|
650
|
+
console.error(`[${progress.phase}] ${progress.percentage}% - ${progress.message}`);
|
|
651
|
+
});
|
|
652
|
+
let statusMessage = `Indexed ${stats.filesIndexed}/${stats.filesScanned} files (${stats.chunksCreated} chunks) in ${(stats.durationMs / 1000).toFixed(1)}s`;
|
|
653
|
+
if (stats.status === "partial") {
|
|
654
|
+
statusMessage += `\n\nWarnings:\n${stats.errors?.join("\n")}`;
|
|
655
|
+
}
|
|
656
|
+
else if (stats.status === "failed") {
|
|
657
|
+
statusMessage = `Indexing failed:\n${stats.errors?.join("\n")}`;
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
content: [
|
|
661
|
+
{
|
|
662
|
+
type: "text",
|
|
663
|
+
text: statusMessage,
|
|
664
|
+
},
|
|
665
|
+
],
|
|
666
|
+
isError: stats.status === "failed",
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
case "search_code": {
|
|
670
|
+
const SearchCodeSchema = z.object({
|
|
671
|
+
path: z.string(),
|
|
672
|
+
query: z.string(),
|
|
673
|
+
limit: z.number().optional(),
|
|
674
|
+
fileTypes: z.array(z.string()).optional(),
|
|
675
|
+
pathPattern: z.string().optional(),
|
|
676
|
+
});
|
|
677
|
+
const { path, query, limit, fileTypes, pathPattern } = SearchCodeSchema.parse(args);
|
|
678
|
+
const results = await codeIndexer.searchCode(path, query, {
|
|
679
|
+
limit,
|
|
680
|
+
fileTypes,
|
|
681
|
+
pathPattern,
|
|
682
|
+
});
|
|
683
|
+
if (results.length === 0) {
|
|
684
|
+
return {
|
|
685
|
+
content: [
|
|
686
|
+
{
|
|
687
|
+
type: "text",
|
|
688
|
+
text: `No results found for query: "${query}"`,
|
|
689
|
+
},
|
|
690
|
+
],
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
// Format results with file references
|
|
694
|
+
const formattedResults = results
|
|
695
|
+
.map((r, idx) => `\n--- Result ${idx + 1} (score: ${r.score.toFixed(3)}) ---\n` +
|
|
696
|
+
`File: ${r.filePath}:${r.startLine}-${r.endLine}\n` +
|
|
697
|
+
`Language: ${r.language}\n\n` +
|
|
698
|
+
`${r.content}\n`)
|
|
699
|
+
.join("\n");
|
|
700
|
+
return {
|
|
701
|
+
content: [
|
|
702
|
+
{
|
|
703
|
+
type: "text",
|
|
704
|
+
text: `Found ${results.length} result(s):\n${formattedResults}`,
|
|
705
|
+
},
|
|
706
|
+
],
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
case "get_index_status": {
|
|
710
|
+
const GetIndexStatusSchema = z.object({
|
|
711
|
+
path: z.string(),
|
|
712
|
+
});
|
|
713
|
+
const { path } = GetIndexStatusSchema.parse(args);
|
|
714
|
+
const status = await codeIndexer.getIndexStatus(path);
|
|
715
|
+
if (!status.isIndexed) {
|
|
716
|
+
return {
|
|
717
|
+
content: [
|
|
718
|
+
{
|
|
719
|
+
type: "text",
|
|
720
|
+
text: `Codebase at "${path}" is not indexed. Use index_codebase to index it first.`,
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
content: [
|
|
727
|
+
{
|
|
728
|
+
type: "text",
|
|
729
|
+
text: JSON.stringify(status, null, 2),
|
|
730
|
+
},
|
|
731
|
+
],
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
case "reindex_changes": {
|
|
735
|
+
const ReindexChangesSchema = z.object({
|
|
736
|
+
path: z.string(),
|
|
737
|
+
});
|
|
738
|
+
const { path } = ReindexChangesSchema.parse(args);
|
|
739
|
+
const stats = await codeIndexer.reindexChanges(path, (progress) => {
|
|
740
|
+
console.error(`[${progress.phase}] ${progress.percentage}% - ${progress.message}`);
|
|
741
|
+
});
|
|
742
|
+
let message = `Incremental re-index complete:\n`;
|
|
743
|
+
message += `- Files added: ${stats.filesAdded}\n`;
|
|
744
|
+
message += `- Files modified: ${stats.filesModified}\n`;
|
|
745
|
+
message += `- Files deleted: ${stats.filesDeleted}\n`;
|
|
746
|
+
message += `- Chunks added: ${stats.chunksAdded}\n`;
|
|
747
|
+
message += `- Duration: ${(stats.durationMs / 1000).toFixed(1)}s`;
|
|
748
|
+
if (stats.filesAdded === 0 && stats.filesModified === 0 && stats.filesDeleted === 0) {
|
|
749
|
+
message = `No changes detected. Codebase is up to date.`;
|
|
750
|
+
}
|
|
751
|
+
return {
|
|
752
|
+
content: [
|
|
753
|
+
{
|
|
754
|
+
type: "text",
|
|
755
|
+
text: message,
|
|
756
|
+
},
|
|
757
|
+
],
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
case "clear_index": {
|
|
761
|
+
const ClearIndexSchema = z.object({
|
|
762
|
+
path: z.string(),
|
|
763
|
+
});
|
|
764
|
+
const { path } = ClearIndexSchema.parse(args);
|
|
765
|
+
await codeIndexer.clearIndex(path);
|
|
766
|
+
return {
|
|
767
|
+
content: [
|
|
768
|
+
{
|
|
769
|
+
type: "text",
|
|
770
|
+
text: `Index cleared for codebase at "${path}".`,
|
|
771
|
+
},
|
|
772
|
+
],
|
|
773
|
+
};
|
|
774
|
+
}
|
|
524
775
|
default:
|
|
525
776
|
return {
|
|
526
777
|
content: [
|