codevault 1.6.1 → 1.7.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/dist/chunking/token-counter.d.ts.map +1 -1
- package/dist/chunking/token-counter.js +2 -31
- package/dist/chunking/token-counter.js.map +1 -1
- package/dist/cli.js +3 -5
- package/dist/cli.js.map +1 -1
- package/dist/codemap/io.d.ts +1 -0
- package/dist/codemap/io.d.ts.map +1 -1
- package/dist/codemap/io.js +16 -0
- package/dist/codemap/io.js.map +1 -1
- package/dist/config/apply-env.d.ts +8 -0
- package/dist/config/apply-env.d.ts.map +1 -1
- package/dist/config/apply-env.js +54 -56
- package/dist/config/apply-env.js.map +1 -1
- package/dist/config/resolver.d.ts +30 -0
- package/dist/config/resolver.d.ts.map +1 -0
- package/dist/config/resolver.js +29 -0
- package/dist/config/resolver.js.map +1 -0
- package/dist/core/IndexerEngine.d.ts +24 -0
- package/dist/core/IndexerEngine.d.ts.map +1 -0
- package/dist/core/IndexerEngine.js +372 -0
- package/dist/core/IndexerEngine.js.map +1 -0
- package/dist/core/SearchService.d.ts +25 -0
- package/dist/core/SearchService.d.ts.map +1 -0
- package/dist/core/SearchService.js +455 -0
- package/dist/core/SearchService.js.map +1 -0
- package/dist/core/batch-indexer.d.ts.map +1 -1
- package/dist/core/batch-indexer.js +7 -4
- package/dist/core/batch-indexer.js.map +1 -1
- package/dist/core/indexer.d.ts +1 -1
- package/dist/core/indexer.d.ts.map +1 -1
- package/dist/core/indexer.js +4 -598
- package/dist/core/indexer.js.map +1 -1
- package/dist/core/indexing/chunk-pipeline.d.ts +39 -0
- package/dist/core/indexing/chunk-pipeline.d.ts.map +1 -0
- package/dist/core/indexing/chunk-pipeline.js +210 -0
- package/dist/core/indexing/chunk-pipeline.js.map +1 -0
- package/dist/core/indexing/file-scanner.d.ts +11 -0
- package/dist/core/indexing/file-scanner.d.ts.map +1 -0
- package/dist/core/indexing/file-scanner.js +49 -0
- package/dist/core/indexing/file-scanner.js.map +1 -0
- package/dist/core/search.d.ts +1 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +11 -540
- package/dist/core/search.js.map +1 -1
- package/dist/database/db.d.ts +2 -3
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +0 -40
- package/dist/database/db.js.map +1 -1
- package/dist/indexer/watch.d.ts.map +1 -1
- package/dist/indexer/watch.js +3 -1
- package/dist/indexer/watch.js.map +1 -1
- package/dist/mcp/handlers/context.d.ts +15 -0
- package/dist/mcp/handlers/context.d.ts.map +1 -0
- package/dist/mcp/handlers/context.js +31 -0
- package/dist/mcp/handlers/context.js.map +1 -0
- package/dist/mcp/handlers/index.d.ts +5 -0
- package/dist/mcp/handlers/index.d.ts.map +1 -0
- package/dist/mcp/handlers/index.js +5 -0
- package/dist/mcp/handlers/index.js.map +1 -0
- package/dist/mcp/handlers/project.d.ts +41 -0
- package/dist/mcp/handlers/project.d.ts.map +1 -0
- package/dist/mcp/handlers/project.js +76 -0
- package/dist/mcp/handlers/project.js.map +1 -0
- package/dist/mcp/handlers/search.d.ts +27 -0
- package/dist/mcp/handlers/search.d.ts.map +1 -0
- package/dist/mcp/handlers/search.js +108 -0
- package/dist/mcp/handlers/search.js.map +1 -0
- package/dist/mcp/handlers/synthesis.d.ts +15 -0
- package/dist/mcp/handlers/synthesis.d.ts.map +1 -0
- package/dist/mcp/handlers/synthesis.js +58 -0
- package/dist/mcp/handlers/synthesis.js.map +1 -0
- package/dist/mcp-server.d.ts +10 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +223 -471
- package/dist/mcp-server.js.map +1 -1
- package/dist/providers/chat-llm.d.ts +7 -2
- package/dist/providers/chat-llm.d.ts.map +1 -1
- package/dist/providers/chat-llm.js +23 -10
- package/dist/providers/chat-llm.js.map +1 -1
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +5 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +16 -6
- package/dist/providers/openai.js.map +1 -1
- package/dist/ranking/api-reranker.d.ts.map +1 -1
- package/dist/ranking/api-reranker.js +4 -1
- package/dist/ranking/api-reranker.js.map +1 -1
- package/dist/ranking/symbol-boost.js +2 -2
- package/dist/ranking/symbol-boost.js.map +1 -1
- package/dist/synthesis/conversational-synthesizer.d.ts.map +1 -1
- package/dist/synthesis/conversational-synthesizer.js +5 -2
- package/dist/synthesis/conversational-synthesizer.js.map +1 -1
- package/dist/synthesis/synthesizer.d.ts.map +1 -1
- package/dist/synthesis/synthesizer.js +5 -2
- package/dist/synthesis/synthesizer.js.map +1 -1
- package/dist/tests/rate-limiter.test.d.ts +2 -0
- package/dist/tests/rate-limiter.test.d.ts.map +1 -0
- package/dist/tests/rate-limiter.test.js +11 -0
- package/dist/tests/rate-limiter.test.js.map +1 -0
- package/dist/tests/search-normalization.test.d.ts +2 -0
- package/dist/tests/search-normalization.test.d.ts.map +1 -0
- package/dist/tests/search-normalization.test.js +9 -0
- package/dist/tests/search-normalization.test.js.map +1 -0
- package/dist/tests/semantic-chunker.test.d.ts +2 -0
- package/dist/tests/semantic-chunker.test.d.ts.map +1 -0
- package/dist/tests/semantic-chunker.test.js +48 -0
- package/dist/tests/semantic-chunker.test.js.map +1 -0
- package/dist/tests/simple-lru.test.d.ts +2 -0
- package/dist/tests/simple-lru.test.d.ts.map +1 -0
- package/dist/tests/simple-lru.test.js +21 -0
- package/dist/tests/simple-lru.test.js.map +1 -0
- package/dist/tests/symbol-boost.test.d.ts +2 -0
- package/dist/tests/symbol-boost.test.d.ts.map +1 -0
- package/dist/tests/symbol-boost.test.js +21 -0
- package/dist/tests/symbol-boost.test.js.map +1 -0
- package/dist/utils/simple-lru.d.ts +10 -0
- package/dist/utils/simple-lru.d.ts.map +1 -0
- package/dist/utils/simple-lru.js +38 -0
- package/dist/utils/simple-lru.js.map +1 -0
- package/package.json +3 -2
package/dist/mcp-server.js
CHANGED
|
@@ -6,506 +6,258 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { resolveProjectRoot } from './utils/path-helpers.js';
|
|
9
|
+
import * as handlers from './mcp/handlers/index.js';
|
|
10
|
+
import { SearchCodeArgsSchema, SearchCodeWithChunksArgsSchema, GetCodeChunkArgsSchema, IndexProjectArgsSchema, UpdateProjectArgsSchema, GetProjectStatsArgsSchema, UseContextPackArgsSchema, AskCodebaseArgsSchema } from './mcp/schemas.js';
|
|
11
|
+
import { CACHE_CONSTANTS } from './config/constants.js';
|
|
12
|
+
import { clearSearchCaches } from './core/search.js';
|
|
13
|
+
import { clearTokenCache } from './chunking/token-counter.js';
|
|
14
|
+
import { logger } from './utils/logger.js';
|
|
16
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
16
|
const __dirname = path.dirname(__filename);
|
|
18
17
|
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Clear search caches
|
|
31
|
-
const searchModule = await import('./core/search.js').catch(() => ({ clearSearchCaches: undefined }));
|
|
32
|
-
if (typeof searchModule.clearSearchCaches === 'function') {
|
|
33
|
-
searchModule.clearSearchCaches();
|
|
34
|
-
}
|
|
35
|
-
// Clear token counter cache
|
|
36
|
-
const tokenModule = await import('./chunking/token-counter.js').catch(() => ({ clearTokenCache: undefined }));
|
|
37
|
-
if (typeof tokenModule.clearTokenCache === 'function') {
|
|
38
|
-
tokenModule.clearTokenCache();
|
|
39
|
-
}
|
|
40
|
-
console.error(JSON.stringify({
|
|
41
|
-
event: 'cache_cleared',
|
|
42
|
-
timestamp: new Date().toISOString()
|
|
43
|
-
}));
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
// Ignore errors during cleanup
|
|
47
|
-
}
|
|
48
|
-
}, CACHE_CLEAR_INTERVAL_MS);
|
|
49
|
-
}
|
|
50
|
-
const server = new Server({
|
|
51
|
-
name: 'codevault-code-memory',
|
|
52
|
-
version: packageJson.version,
|
|
53
|
-
}, {
|
|
54
|
-
capabilities: {
|
|
55
|
-
tools: {},
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
// List all available tools
|
|
59
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
60
|
-
const tools = [
|
|
61
|
-
{
|
|
62
|
-
name: 'search_code',
|
|
63
|
-
description: 'Search code semantically using embeddings',
|
|
64
|
-
inputSchema: {
|
|
65
|
-
type: 'object',
|
|
66
|
-
properties: {
|
|
67
|
-
query: { type: 'string', description: 'Search query' },
|
|
68
|
-
limit: { type: 'number', description: 'Max results (default: 50, max: 200)', default: 50 },
|
|
69
|
-
provider: { type: 'string', description: 'Embedding provider (auto|openai)', default: 'auto' },
|
|
70
|
-
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
71
|
-
path_glob: { type: ['string', 'array'], description: 'File patterns to filter' },
|
|
72
|
-
tags: { type: ['string', 'array'], description: 'Tags to filter' },
|
|
73
|
-
lang: { type: ['string', 'array'], description: 'Languages to filter' },
|
|
74
|
-
reranker: { type: 'string', enum: ['off', 'api'], default: 'off' },
|
|
75
|
-
hybrid: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
76
|
-
bm25: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
77
|
-
symbol_boost: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
78
|
-
},
|
|
79
|
-
required: ['query'],
|
|
18
|
+
export class McpServer {
|
|
19
|
+
server;
|
|
20
|
+
sessionContextPack = null;
|
|
21
|
+
cacheCleanupTimer = null;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.server = new Server({
|
|
24
|
+
name: 'codevault-code-memory',
|
|
25
|
+
version: packageJson.version,
|
|
26
|
+
}, {
|
|
27
|
+
capabilities: {
|
|
28
|
+
tools: {},
|
|
80
29
|
},
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
30
|
+
});
|
|
31
|
+
this.setupHandlers();
|
|
32
|
+
}
|
|
33
|
+
setupHandlers() {
|
|
34
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
35
|
+
const tools = [
|
|
36
|
+
{
|
|
37
|
+
name: 'search_code',
|
|
38
|
+
description: 'Search code semantically using embeddings',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
query: { type: 'string', description: 'Search query' },
|
|
43
|
+
limit: { type: 'number', description: 'Max results (default: 50, max: 200)', default: 50 },
|
|
44
|
+
provider: { type: 'string', description: 'Embedding provider (auto|openai)', default: 'auto' },
|
|
45
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
46
|
+
path_glob: { type: ['string', 'array'], description: 'File patterns to filter' },
|
|
47
|
+
tags: { type: ['string', 'array'], description: 'Tags to filter' },
|
|
48
|
+
lang: { type: ['string', 'array'], description: 'Languages to filter' },
|
|
49
|
+
reranker: { type: 'string', enum: ['off', 'api'], default: 'off' },
|
|
50
|
+
hybrid: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
51
|
+
bm25: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
52
|
+
symbol_boost: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
53
|
+
},
|
|
54
|
+
required: ['query'],
|
|
55
|
+
},
|
|
99
56
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
57
|
+
{
|
|
58
|
+
name: 'search_code_with_chunks',
|
|
59
|
+
description: 'Search code and return full code chunks',
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
query: { type: 'string', description: 'Search query' },
|
|
64
|
+
limit: { type: 'number', description: 'Max results', default: 10 },
|
|
65
|
+
provider: { type: 'string', default: 'auto' },
|
|
66
|
+
path: { type: 'string', default: '.' },
|
|
67
|
+
path_glob: { type: ['string', 'array'] },
|
|
68
|
+
tags: { type: ['string', 'array'] },
|
|
69
|
+
lang: { type: ['string', 'array'] },
|
|
70
|
+
reranker: { type: 'string', enum: ['off', 'api'], default: 'off' },
|
|
71
|
+
hybrid: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
72
|
+
bm25: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
73
|
+
symbol_boost: { type: 'string', enum: ['on', 'off'], default: 'on' },
|
|
74
|
+
},
|
|
75
|
+
required: ['query'],
|
|
76
|
+
},
|
|
111
77
|
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
78
|
+
{
|
|
79
|
+
name: 'get_code_chunk',
|
|
80
|
+
description: 'Get code chunk by SHA',
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
sha: { type: 'string', description: 'SHA of code chunk' },
|
|
85
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
86
|
+
},
|
|
87
|
+
required: ['sha'],
|
|
88
|
+
},
|
|
123
89
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
90
|
+
{
|
|
91
|
+
name: 'index_project',
|
|
92
|
+
description: 'Index a project for semantic search',
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
97
|
+
provider: { type: 'string', description: 'Embedding provider', default: 'auto' },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
134
100
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
101
|
+
{
|
|
102
|
+
name: 'update_project',
|
|
103
|
+
description: 'Update project index',
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
108
|
+
provider: { type: 'string', default: 'auto' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
144
111
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
112
|
+
{
|
|
113
|
+
name: 'get_project_stats',
|
|
114
|
+
description: 'Get project statistics',
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
155
121
|
},
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
chat_provider: { type: 'string', description: 'Chat LLM provider (auto|openai)', default: 'auto' },
|
|
168
|
-
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
169
|
-
max_chunks: { type: 'number', description: 'Max code chunks to analyze', default: 10 },
|
|
170
|
-
path_glob: { type: ['string', 'array'], description: 'File patterns to filter' },
|
|
171
|
-
tags: { type: ['string', 'array'], description: 'Tags to filter' },
|
|
172
|
-
lang: { type: ['string', 'array'], description: 'Languages to filter' },
|
|
173
|
-
reranker: { type: 'string', enum: ['on', 'off'], default: 'on', description: 'Use API reranking' },
|
|
174
|
-
multi_query: { type: 'boolean', default: false, description: 'Break complex questions into sub-queries' },
|
|
175
|
-
temperature: { type: 'number', minimum: 0, maximum: 2, default: 0.7, description: 'LLM temperature' },
|
|
122
|
+
{
|
|
123
|
+
name: 'use_context_pack',
|
|
124
|
+
description: 'Activate a context pack for scoped search',
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
name: { type: 'string', description: 'Context pack name or "clear"' },
|
|
129
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
130
|
+
},
|
|
131
|
+
required: ['name'],
|
|
132
|
+
},
|
|
176
133
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
lang: typedArgs.lang,
|
|
195
|
-
reranker: typedArgs.reranker,
|
|
196
|
-
hybrid: typedArgs.hybrid,
|
|
197
|
-
bm25: typedArgs.bm25,
|
|
198
|
-
symbol_boost: typedArgs.symbol_boost,
|
|
199
|
-
}, { basePath: cleanPath, sessionPack: sessionContextPack });
|
|
200
|
-
const results = await searchCode(typedArgs.query, typedArgs.limit || 50, typedArgs.provider || 'auto', cleanPath, scopeFilters);
|
|
201
|
-
if (!results.success) {
|
|
202
|
-
return {
|
|
203
|
-
content: [
|
|
204
|
-
{
|
|
205
|
-
type: 'text',
|
|
206
|
-
text: results.error === 'database_not_found'
|
|
207
|
-
? `📋 Project not indexed!\n\n🔍 Database not found: ${cleanPath}/.codevault/codevault.db\n\n💡 Use index_project tool`
|
|
208
|
-
: `No results: ${results.message}\n${results.suggestion || ''}`,
|
|
209
|
-
},
|
|
210
|
-
],
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
const resultText = results.results
|
|
214
|
-
.map((result, index) => `${index + 1}. ${result.path}\n Symbol: ${result.meta.symbol} (${result.lang})\n Similarity: ${result.meta.score}\n SHA: ${result.sha}`)
|
|
215
|
-
.join('\n\n');
|
|
216
|
-
return {
|
|
217
|
-
content: [
|
|
218
|
-
{
|
|
219
|
-
type: 'text',
|
|
220
|
-
text: `Found ${results.results.length} results for: "${typedArgs.query}"\nProvider: ${results.provider}\n\n${resultText}`,
|
|
134
|
+
{
|
|
135
|
+
name: 'ask_codebase',
|
|
136
|
+
description: 'Ask a question and get LLM-synthesized answer with code citations',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
question: { type: 'string', description: 'Natural language question about the codebase' },
|
|
141
|
+
provider: { type: 'string', description: 'Embedding provider (auto|openai)', default: 'auto' },
|
|
142
|
+
chat_provider: { type: 'string', description: 'Chat LLM provider (auto|openai)', default: 'auto' },
|
|
143
|
+
path: { type: 'string', description: 'Project root directory', default: '.' },
|
|
144
|
+
max_chunks: { type: 'number', description: 'Max code chunks to analyze', default: 10 },
|
|
145
|
+
path_glob: { type: ['string', 'array'], description: 'File patterns to filter' },
|
|
146
|
+
tags: { type: ['string', 'array'], description: 'Tags to filter' },
|
|
147
|
+
lang: { type: ['string', 'array'], description: 'Languages to filter' },
|
|
148
|
+
reranker: { type: 'string', enum: ['on', 'off'], default: 'on', description: 'Use API reranking' },
|
|
149
|
+
multi_query: { type: 'boolean', default: false, description: 'Break complex questions into sub-queries' },
|
|
150
|
+
temperature: { type: 'number', minimum: 0, maximum: 2, default: 0.7, description: 'LLM temperature' },
|
|
221
151
|
},
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const searchResults = await searchCode(typedArgs.query, typedArgs.limit || 10, typedArgs.provider || 'auto', cleanPath, scopeFilters);
|
|
237
|
-
if (!searchResults.success) {
|
|
238
|
-
return {
|
|
239
|
-
content: [{ type: 'text', text: searchResults.message || 'Search failed' }],
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
const resultsWithCode = [];
|
|
243
|
-
for (const result of searchResults.results) {
|
|
244
|
-
const chunkResult = await getChunk(result.sha, cleanPath);
|
|
245
|
-
let code = '';
|
|
246
|
-
let truncated = false;
|
|
247
|
-
if (chunkResult.success && chunkResult.code) {
|
|
248
|
-
code = chunkResult.code;
|
|
249
|
-
if (code.length > MAX_CHUNK_SIZE) {
|
|
250
|
-
code = code.substring(0, MAX_CHUNK_SIZE);
|
|
251
|
-
truncated = true;
|
|
252
|
-
}
|
|
152
|
+
required: ['question'],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
return { tools };
|
|
157
|
+
});
|
|
158
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
159
|
+
const { name, arguments: args } = request.params;
|
|
160
|
+
const rawArgs = args || {};
|
|
161
|
+
try {
|
|
162
|
+
switch (name) {
|
|
163
|
+
case 'search_code': {
|
|
164
|
+
const validArgs = SearchCodeArgsSchema.parse(rawArgs);
|
|
165
|
+
return await handlers.handleSearchCode(validArgs, this.sessionContextPack);
|
|
253
166
|
}
|
|
254
|
-
|
|
255
|
-
|
|
167
|
+
case 'search_code_with_chunks': {
|
|
168
|
+
const validArgs = SearchCodeWithChunksArgsSchema.parse(rawArgs);
|
|
169
|
+
return await handlers.handleSearchCodeWithChunks(validArgs, this.sessionContextPack);
|
|
256
170
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
],
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
return {
|
|
289
|
-
content: [{ type: 'text', text: codeText }],
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
case 'index_project': {
|
|
293
|
-
const cleanPath = resolveProjectRoot(typedArgs);
|
|
294
|
-
if (!fs.existsSync(cleanPath)) {
|
|
295
|
-
throw new Error(`Directory ${cleanPath} does not exist`);
|
|
296
|
-
}
|
|
297
|
-
const result = await indexProject({ repoPath: cleanPath, provider: typedArgs.provider || 'auto' });
|
|
298
|
-
if (!result.success) {
|
|
299
|
-
return {
|
|
300
|
-
content: [
|
|
301
|
-
{ type: 'text', text: `Indexing failed: ${result.errors[0]?.error || 'Unknown error'}` },
|
|
302
|
-
],
|
|
303
|
-
isError: true,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
return {
|
|
307
|
-
content: [
|
|
308
|
-
{
|
|
309
|
-
type: 'text',
|
|
310
|
-
text: `✅ Project indexed successfully!\n\n📊 Statistics:\n- Processed chunks: ${result.processedChunks}\n- Total chunks: ${result.totalChunks}\n- Provider: ${result.provider}\n\n🔍 Ready to search!\n- Quick search: search_code with path="${cleanPath}"\n- With code: search_code_with_chunks with path="${cleanPath}"`,
|
|
311
|
-
},
|
|
312
|
-
],
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
case 'update_project': {
|
|
316
|
-
const cleanPath = resolveProjectRoot(typedArgs);
|
|
317
|
-
const result = await indexProject({ repoPath: cleanPath, provider: typedArgs.provider || 'auto' });
|
|
318
|
-
if (!result.success) {
|
|
319
|
-
return {
|
|
320
|
-
content: [
|
|
321
|
-
{ type: 'text', text: `Update failed: ${result.errors[0]?.error || 'Unknown error'}` },
|
|
322
|
-
],
|
|
323
|
-
isError: true,
|
|
324
|
-
};
|
|
171
|
+
case 'get_code_chunk': {
|
|
172
|
+
const validArgs = GetCodeChunkArgsSchema.parse(rawArgs);
|
|
173
|
+
return await handlers.handleGetCodeChunk(validArgs);
|
|
174
|
+
}
|
|
175
|
+
case 'index_project': {
|
|
176
|
+
const validArgs = IndexProjectArgsSchema.parse(rawArgs);
|
|
177
|
+
return await handlers.handleIndexProject(validArgs);
|
|
178
|
+
}
|
|
179
|
+
case 'update_project': {
|
|
180
|
+
const validArgs = UpdateProjectArgsSchema.parse(rawArgs);
|
|
181
|
+
return await handlers.handleUpdateProject(validArgs);
|
|
182
|
+
}
|
|
183
|
+
case 'get_project_stats': {
|
|
184
|
+
const validArgs = GetProjectStatsArgsSchema.parse(rawArgs);
|
|
185
|
+
return await handlers.handleGetProjectStats(validArgs);
|
|
186
|
+
}
|
|
187
|
+
case 'use_context_pack': {
|
|
188
|
+
const validArgs = UseContextPackArgsSchema.parse(rawArgs);
|
|
189
|
+
return await handlers.handleUseContextPack(validArgs, (pack) => {
|
|
190
|
+
this.sessionContextPack = pack;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
case 'ask_codebase': {
|
|
194
|
+
const validArgs = AskCodebaseArgsSchema.parse(rawArgs);
|
|
195
|
+
return await handlers.handleAskCodebase(validArgs, this.sessionContextPack);
|
|
196
|
+
}
|
|
197
|
+
default:
|
|
198
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
325
199
|
}
|
|
326
|
-
return {
|
|
327
|
-
content: [
|
|
328
|
-
{
|
|
329
|
-
type: 'text',
|
|
330
|
-
text: `🔄 Project updated!\n📊 Processed: ${result.processedChunks} chunks\n📁 Total: ${result.totalChunks} chunks`,
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
};
|
|
334
200
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (!overviewResult.success) {
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (error && typeof error === 'object' && 'issues' in error && Array.isArray(error.issues)) {
|
|
203
|
+
const validationError = `Validation Error: ${error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join(', ')}`;
|
|
339
204
|
return {
|
|
340
|
-
content: [
|
|
341
|
-
{ type: 'text', text: `Error: ${overviewResult.message || 'Failed to get stats'}` },
|
|
342
|
-
],
|
|
205
|
+
content: [{ type: 'text', text: validationError }],
|
|
343
206
|
isError: true,
|
|
344
207
|
};
|
|
345
208
|
}
|
|
346
|
-
if (overviewResult.results.length === 0) {
|
|
347
|
-
return {
|
|
348
|
-
content: [{ type: 'text', text: '📋 Project not indexed or empty' }],
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
const overview = overviewResult.results
|
|
352
|
-
.map((result) => `- ${result.path} :: ${result.meta.symbol} (${result.lang})`)
|
|
353
|
-
.join('\n');
|
|
354
209
|
return {
|
|
355
|
-
content: [
|
|
356
|
-
|
|
357
|
-
type: 'text',
|
|
358
|
-
text: `📊 Project overview (${overviewResult.results.length} main functions):\n\n${overview}`,
|
|
359
|
-
},
|
|
360
|
-
],
|
|
210
|
+
content: [{ type: 'text', text: `ERROR: ${error.message}` }],
|
|
211
|
+
isError: true,
|
|
361
212
|
};
|
|
362
213
|
}
|
|
363
|
-
|
|
364
|
-
const cleanPath = resolveProjectRoot(typedArgs);
|
|
365
|
-
const name = typedArgs.name;
|
|
366
|
-
if (name === 'default' || name === 'none' || name === 'clear') {
|
|
367
|
-
sessionContextPack = null;
|
|
368
|
-
return {
|
|
369
|
-
content: [{ type: 'text', text: 'Cleared active context pack for this session' }],
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
try {
|
|
373
|
-
const { loadContextPack } = await import('./context/packs.js');
|
|
374
|
-
const pack = loadContextPack(name, cleanPath);
|
|
375
|
-
sessionContextPack = { ...pack, basePath: cleanPath };
|
|
376
|
-
return {
|
|
377
|
-
content: [
|
|
378
|
-
{
|
|
379
|
-
type: 'text',
|
|
380
|
-
text: `Context pack "${pack.key}" activated for session\n\nScope: ${JSON.stringify(pack.scope, null, 2)}`,
|
|
381
|
-
},
|
|
382
|
-
],
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
catch (error) {
|
|
386
|
-
return {
|
|
387
|
-
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
388
|
-
isError: true,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
case 'ask_codebase': {
|
|
393
|
-
const cleanPath = resolveProjectRoot(typedArgs);
|
|
394
|
-
// Use resolveScopeWithPack like other search tools for consistency
|
|
395
|
-
const { scope: scopeFilters } = resolveScopeWithPack({
|
|
396
|
-
path_glob: typedArgs.path_glob,
|
|
397
|
-
tags: typedArgs.tags,
|
|
398
|
-
lang: typedArgs.lang,
|
|
399
|
-
reranker: typedArgs.reranker,
|
|
400
|
-
}, { basePath: cleanPath, sessionPack: sessionContextPack });
|
|
401
|
-
try {
|
|
402
|
-
const result = await synthesizeAnswer(typedArgs.question, {
|
|
403
|
-
provider: typedArgs.provider || 'auto',
|
|
404
|
-
chatProvider: typedArgs.chat_provider || 'auto',
|
|
405
|
-
workingPath: cleanPath,
|
|
406
|
-
scope: scopeFilters,
|
|
407
|
-
maxChunks: typedArgs.max_chunks || 10,
|
|
408
|
-
useReranking: typedArgs.reranker !== 'off',
|
|
409
|
-
useMultiQuery: typedArgs.multi_query || false,
|
|
410
|
-
temperature: typedArgs.temperature || 0.7
|
|
411
|
-
});
|
|
412
|
-
if (!result.success) {
|
|
413
|
-
let errorText;
|
|
414
|
-
if (result.error === 'no_results') {
|
|
415
|
-
errorText = formatNoResultsMessage(result.query, result.queriesUsed);
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
errorText = formatErrorMessage(result.error || 'Unknown error', result.query);
|
|
419
|
-
}
|
|
420
|
-
return {
|
|
421
|
-
content: [{ type: 'text', text: errorText }],
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
const formattedResult = formatSynthesisResult(result, {
|
|
425
|
-
includeMetadata: true,
|
|
426
|
-
includeStats: true
|
|
427
|
-
});
|
|
428
|
-
return {
|
|
429
|
-
content: [
|
|
430
|
-
{
|
|
431
|
-
type: 'text',
|
|
432
|
-
text: formattedResult
|
|
433
|
-
}
|
|
434
|
-
],
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
catch (error) {
|
|
438
|
-
const errorText = formatErrorMessage(error.message, typedArgs.question);
|
|
439
|
-
return {
|
|
440
|
-
content: [{ type: 'text', text: errorText }],
|
|
441
|
-
isError: true,
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
default:
|
|
446
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
447
|
-
}
|
|
214
|
+
});
|
|
448
215
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
216
|
+
async start() {
|
|
217
|
+
const transport = new StdioServerTransport();
|
|
218
|
+
await this.server.connect(transport);
|
|
219
|
+
logger.info('CodeVault MCP Server started', { version: packageJson.version });
|
|
220
|
+
this.scheduleCacheCleanup();
|
|
221
|
+
this.setupShutdownHandlers();
|
|
454
222
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
await server.connect(transport);
|
|
459
|
-
console.error(JSON.stringify({
|
|
460
|
-
start: 'CodeVault MCP Server started',
|
|
461
|
-
version: packageJson.version,
|
|
462
|
-
}));
|
|
463
|
-
// FIX: Start periodic cache cleanup
|
|
464
|
-
scheduleCacheCleanup();
|
|
465
|
-
// FIX: Add cleanup handlers for graceful shutdown
|
|
466
|
-
const cleanup = async () => {
|
|
467
|
-
// Stop cache cleanup timer
|
|
468
|
-
if (cacheCleanupTimer) {
|
|
469
|
-
clearInterval(cacheCleanupTimer);
|
|
470
|
-
cacheCleanupTimer = null;
|
|
223
|
+
scheduleCacheCleanup() {
|
|
224
|
+
if (this.cacheCleanupTimer) {
|
|
225
|
+
clearInterval(this.cacheCleanupTimer);
|
|
471
226
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (typeof searchModule.clearSearchCaches === 'function') {
|
|
478
|
-
searchModule.clearSearchCaches();
|
|
227
|
+
this.cacheCleanupTimer = setInterval(() => {
|
|
228
|
+
try {
|
|
229
|
+
clearSearchCaches();
|
|
230
|
+
clearTokenCache();
|
|
231
|
+
logger.debug('Cache cleared periodically');
|
|
479
232
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
// Ignore if module doesn't export the function yet
|
|
483
|
-
}
|
|
484
|
-
// Clear token counter cache
|
|
485
|
-
try {
|
|
486
|
-
const tokenModule = await import('./chunking/token-counter.js').catch(() => ({ clearTokenCache: undefined }));
|
|
487
|
-
if (typeof tokenModule.clearTokenCache === 'function') {
|
|
488
|
-
tokenModule.clearTokenCache();
|
|
233
|
+
catch (error) {
|
|
234
|
+
// Ignore errors during cleanup
|
|
489
235
|
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
236
|
+
}, CACHE_CONSTANTS.CACHE_CLEAR_INTERVAL_MS);
|
|
237
|
+
}
|
|
238
|
+
setupShutdownHandlers() {
|
|
239
|
+
const cleanup = async () => {
|
|
240
|
+
if (this.cacheCleanupTimer) {
|
|
241
|
+
clearInterval(this.cacheCleanupTimer);
|
|
242
|
+
this.cacheCleanupTimer = null;
|
|
243
|
+
}
|
|
244
|
+
this.sessionContextPack = null;
|
|
245
|
+
clearSearchCaches();
|
|
246
|
+
clearTokenCache();
|
|
247
|
+
};
|
|
248
|
+
process.on('SIGINT', async () => {
|
|
249
|
+
await cleanup();
|
|
250
|
+
process.exit(0);
|
|
251
|
+
});
|
|
252
|
+
process.on('SIGTERM', async () => {
|
|
253
|
+
await cleanup();
|
|
254
|
+
process.exit(0);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
506
257
|
}
|
|
507
|
-
|
|
508
|
-
|
|
258
|
+
const server = new McpServer();
|
|
259
|
+
server.start().catch((error) => {
|
|
260
|
+
logger.error('Fatal error', error);
|
|
509
261
|
process.exit(1);
|
|
510
262
|
});
|
|
511
263
|
//# sourceMappingURL=mcp-server.js.map
|