smart-coding-mcp 1.4.0 → 2.0.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/README.md +118 -65
- package/config.json +4 -1
- package/features/check-last-version.js +4 -3
- package/features/get-status.js +132 -0
- package/features/index-codebase.js +8 -0
- package/features/set-workspace.js +155 -0
- package/index.js +22 -3
- package/lib/ast-chunker.js +273 -0
- package/lib/config.js +40 -1
- package/lib/embedding-worker.js +29 -2
- package/lib/mrl-embedder.js +133 -0
- package/lib/tokenizer.js +4 -0
- package/package.json +5 -3
- package/test/ast-chunker.test.js +105 -0
- package/test/check-last-version.test.js +2 -2
- package/test/device-detection.test.js +110 -0
- package/test/embedding-model.test.js +14 -11
- package/test/helpers.js +3 -3
- package/test/mrl-embedder.test.js +108 -0
package/README.md
CHANGED
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
|
|
8
|
-
An extensible Model Context Protocol (MCP) server that provides intelligent semantic code search for AI assistants. Built with local AI models (
|
|
8
|
+
An extensible Model Context Protocol (MCP) server that provides intelligent semantic code search for AI assistants. Built with local AI models using Matryoshka Representation Learning (MRL) for flexible embedding dimensions (64-768d), with runtime workspace switching and comprehensive status reporting.
|
|
9
9
|
|
|
10
10
|
### Available Tools
|
|
11
11
|
|
|
12
|
-
| Tool | Description | Example
|
|
13
|
-
| ---------------------- | ------------------------------------------------- |
|
|
14
|
-
| `semantic_search` | Find code by meaning, not just keywords | `"Where do we validate user input?"`
|
|
15
|
-
| `index_codebase` | Manually trigger reindexing | Use after major refactoring or branch switches
|
|
16
|
-
| `clear_cache` | Reset the embeddings cache | Useful when cache becomes corrupted
|
|
17
|
-
| `d_check_last_version` | Get latest version of any package (20 ecosystems) | `"express"`, `"npm:react"`, `"pip:requests"`
|
|
12
|
+
| Tool | Description | Example |
|
|
13
|
+
| ---------------------- | ------------------------------------------------- | ----------------------------------------------- |
|
|
14
|
+
| `semantic_search` | Find code by meaning, not just keywords | `"Where do we validate user input?"` |
|
|
15
|
+
| `index_codebase` | Manually trigger reindexing | Use after major refactoring or branch switches |
|
|
16
|
+
| `clear_cache` | Reset the embeddings cache | Useful when cache becomes corrupted |
|
|
17
|
+
| `d_check_last_version` | Get latest version of any package (20 ecosystems) | `"express"`, `"npm:react"`, `"pip:requests"` |
|
|
18
|
+
| `e_set_workspace` | Change project path at runtime | Switch to different project without restart |
|
|
19
|
+
| `f_get_status` | Get server info: version, index status, config | Check indexing progress, model info, cache size |
|
|
18
20
|
|
|
19
21
|
## What This Does
|
|
20
22
|
|
|
@@ -72,92 +74,80 @@ Add to your MCP configuration file. The location depends on your IDE and OS:
|
|
|
72
74
|
|
|
73
75
|
Add the server configuration to the `mcpServers` object in your config file:
|
|
74
76
|
|
|
75
|
-
### Option 1:
|
|
76
|
-
|
|
77
|
-
By default, the server indexes the directory it is started in. Most clients start MCP servers in the workspace root automatically:
|
|
77
|
+
### Option 1: Absolute Path (Recommended)
|
|
78
78
|
|
|
79
79
|
```json
|
|
80
80
|
{
|
|
81
81
|
"mcpServers": {
|
|
82
82
|
"smart-coding-mcp": {
|
|
83
83
|
"command": "smart-coding-mcp",
|
|
84
|
-
"args": []
|
|
84
|
+
"args": ["--workspace", "/absolute/path/to/your/project"]
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
### Option 2: Multi-Project Support
|
|
91
91
|
|
|
92
92
|
```json
|
|
93
93
|
{
|
|
94
94
|
"mcpServers": {
|
|
95
|
-
"smart-coding-mcp": {
|
|
95
|
+
"smart-coding-mcp-frontend": {
|
|
96
96
|
"command": "smart-coding-mcp",
|
|
97
|
-
"args": ["--workspace", "
|
|
97
|
+
"args": ["--workspace", "/path/to/frontend"]
|
|
98
|
+
},
|
|
99
|
+
"smart-coding-mcp-backend": {
|
|
100
|
+
"command": "smart-coding-mcp",
|
|
101
|
+
"args": ["--workspace", "/path/to/backend"]
|
|
98
102
|
}
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
```
|
|
102
106
|
|
|
103
|
-
|
|
107
|
+
### Option 3: Auto-Detection (May Not Work)
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
| ---------------- | ----------------------------- |
|
|
107
|
-
| VS Code | Yes |
|
|
108
|
-
| Cursor (Cascade) | Yes |
|
|
109
|
-
| Antigravity | Yes |
|
|
110
|
-
| Claude Desktop | No (use Option 2) |
|
|
111
|
-
|
|
112
|
-
### Option 2: Absolute Path (Claude Desktop)
|
|
109
|
+
> ⚠️ **Warning:** Most MCP clients (including Antigravity and Claude Desktop) do NOT support `${workspaceFolder}` variable expansion. The server will exit with an error if the variable is not expanded.
|
|
113
110
|
|
|
114
|
-
For clients that
|
|
111
|
+
For clients that support dynamic variables (VS Code, Cursor):
|
|
115
112
|
|
|
116
113
|
```json
|
|
117
114
|
{
|
|
118
115
|
"mcpServers": {
|
|
119
116
|
"smart-coding-mcp": {
|
|
120
117
|
"command": "smart-coding-mcp",
|
|
121
|
-
"args": ["--workspace", "
|
|
118
|
+
"args": ["--workspace", "${workspaceFolder}"]
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
122
|
```
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"command": "smart-coding-mcp",
|
|
134
|
-
"args": ["--workspace", "/path/to/frontend"]
|
|
135
|
-
},
|
|
136
|
-
"smart-coding-mcp-backend": {
|
|
137
|
-
"command": "smart-coding-mcp",
|
|
138
|
-
"args": ["--workspace", "/path/to/backend"]
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
124
|
+
| Client | Supports `${workspaceFolder}` |
|
|
125
|
+
| ---------------- | ----------------------------- |
|
|
126
|
+
| VS Code | Yes |
|
|
127
|
+
| Cursor (Cascade) | Yes |
|
|
128
|
+
| Antigravity | No ❌ |
|
|
129
|
+
| Claude Desktop | No ❌ |
|
|
143
130
|
|
|
144
131
|
## Environment Variables
|
|
145
132
|
|
|
146
133
|
Override configuration settings via environment variables in your MCP config:
|
|
147
134
|
|
|
148
|
-
| Variable
|
|
149
|
-
|
|
|
150
|
-
| `SMART_CODING_VERBOSE`
|
|
151
|
-
| `SMART_CODING_BATCH_SIZE`
|
|
152
|
-
| `SMART_CODING_MAX_FILE_SIZE`
|
|
153
|
-
| `SMART_CODING_CHUNK_SIZE`
|
|
154
|
-
| `SMART_CODING_MAX_RESULTS`
|
|
155
|
-
| `SMART_CODING_SMART_INDEXING`
|
|
156
|
-
| `SMART_CODING_WATCH_FILES`
|
|
157
|
-
| `SMART_CODING_SEMANTIC_WEIGHT`
|
|
158
|
-
| `SMART_CODING_EXACT_MATCH_BOOST`
|
|
159
|
-
| `SMART_CODING_EMBEDDING_MODEL`
|
|
160
|
-
| `
|
|
135
|
+
| Variable | Type | Default | Description |
|
|
136
|
+
| ---------------------------------- | ------- | -------------------------------- | ------------------------------------------ |
|
|
137
|
+
| `SMART_CODING_VERBOSE` | boolean | `false` | Enable detailed logging |
|
|
138
|
+
| `SMART_CODING_BATCH_SIZE` | number | `100` | Files to process in parallel |
|
|
139
|
+
| `SMART_CODING_MAX_FILE_SIZE` | number | `1048576` | Max file size in bytes (1MB) |
|
|
140
|
+
| `SMART_CODING_CHUNK_SIZE` | number | `25` | Lines of code per chunk |
|
|
141
|
+
| `SMART_CODING_MAX_RESULTS` | number | `5` | Max search results |
|
|
142
|
+
| `SMART_CODING_SMART_INDEXING` | boolean | `true` | Enable smart project detection |
|
|
143
|
+
| `SMART_CODING_WATCH_FILES` | boolean | `false` | Enable file watching for auto-reindex |
|
|
144
|
+
| `SMART_CODING_SEMANTIC_WEIGHT` | number | `0.7` | Weight for semantic similarity (0-1) |
|
|
145
|
+
| `SMART_CODING_EXACT_MATCH_BOOST` | number | `1.5` | Boost for exact text matches |
|
|
146
|
+
| `SMART_CODING_EMBEDDING_MODEL` | string | `nomic-ai/nomic-embed-text-v1.5` | AI embedding model to use |
|
|
147
|
+
| `SMART_CODING_EMBEDDING_DIMENSION` | number | `256` | MRL dimension (64, 128, 256, 512, 768) |
|
|
148
|
+
| `SMART_CODING_DEVICE` | string | `cpu` | Inference device (`cpu`, `webgpu`, `auto`) |
|
|
149
|
+
| `SMART_CODING_CHUNKING_MODE` | string | `smart` | Code chunking (`smart`, `ast`, `line`) |
|
|
150
|
+
| `SMART_CODING_WORKER_THREADS` | string | `auto` | Worker threads (`auto` or 1-32) |
|
|
161
151
|
|
|
162
152
|
**Example with environment variables:**
|
|
163
153
|
|
|
@@ -181,16 +171,72 @@ Override configuration settings via environment variables in your MCP config:
|
|
|
181
171
|
|
|
182
172
|
## How It Works
|
|
183
173
|
|
|
184
|
-
|
|
174
|
+
```mermaid
|
|
175
|
+
flowchart TB
|
|
176
|
+
subgraph IDE["IDE / AI Assistant"]
|
|
177
|
+
Agent["AI Agent<br/>(Claude, GPT, Gemini)"]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
subgraph MCP["Smart Coding MCP Server"]
|
|
181
|
+
direction TB
|
|
182
|
+
Protocol["Model Context Protocol<br/>JSON-RPC over stdio"]
|
|
183
|
+
Tools["MCP Tools<br/>semantic_search | index_codebase | set_workspace | get_status"]
|
|
184
|
+
|
|
185
|
+
subgraph Indexing["Indexing Pipeline"]
|
|
186
|
+
Discovery["File Discovery<br/>glob patterns + smart ignore"]
|
|
187
|
+
Chunking["Code Chunking<br/>Smart (regex) / AST (Tree-sitter)"]
|
|
188
|
+
Embedding["AI Embedding<br/>transformers.js + ONNX Runtime"]
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
subgraph AI["AI Model"]
|
|
192
|
+
Model["nomic-embed-text-v1.5<br/>Matryoshka Representation Learning"]
|
|
193
|
+
Dimensions["Flexible Dimensions<br/>64 | 128 | 256 | 512 | 768"]
|
|
194
|
+
Normalize["Layer Norm → Slice → L2 Normalize"]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
subgraph Search["Search"]
|
|
198
|
+
QueryEmbed["Query → Vector"]
|
|
199
|
+
Cosine["Cosine Similarity"]
|
|
200
|
+
Hybrid["Hybrid Search<br/>Semantic + Exact Match Boost"]
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
subgraph Storage["Cache"]
|
|
205
|
+
Vectors["Vector Store<br/>embeddings.json"]
|
|
206
|
+
Hashes["File Hashes<br/>Incremental updates"]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
Agent <-->|"MCP Protocol"| Protocol
|
|
210
|
+
Protocol --> Tools
|
|
211
|
+
|
|
212
|
+
Tools --> Discovery
|
|
213
|
+
Discovery --> Chunking
|
|
214
|
+
Chunking --> Embedding
|
|
215
|
+
Embedding --> Model
|
|
216
|
+
Model --> Dimensions
|
|
217
|
+
Dimensions --> Normalize
|
|
218
|
+
Normalize --> Vectors
|
|
219
|
+
|
|
220
|
+
Tools --> QueryEmbed
|
|
221
|
+
QueryEmbed --> Model
|
|
222
|
+
Cosine --> Hybrid
|
|
223
|
+
Vectors --> Cosine
|
|
224
|
+
Hybrid --> Agent
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Tech Stack
|
|
185
228
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
229
|
+
| Component | Technology |
|
|
230
|
+
| ------------- | ------------------------------------- |
|
|
231
|
+
| **Protocol** | Model Context Protocol (JSON-RPC) |
|
|
232
|
+
| **AI Model** | nomic-embed-text-v1.5 (MRL) |
|
|
233
|
+
| **Inference** | transformers.js + ONNX Runtime |
|
|
234
|
+
| **Chunking** | Smart regex / Tree-sitter AST |
|
|
235
|
+
| **Search** | Cosine similarity + exact match boost |
|
|
190
236
|
|
|
191
|
-
|
|
237
|
+
### Search Flow
|
|
192
238
|
|
|
193
|
-
|
|
239
|
+
Query → Vector embedding → Cosine similarity → Ranked results
|
|
194
240
|
|
|
195
241
|
## Examples
|
|
196
242
|
|
|
@@ -229,11 +275,18 @@ Finds all try/catch blocks and error handling patterns.
|
|
|
229
275
|
|
|
230
276
|
## Technical Details
|
|
231
277
|
|
|
232
|
-
**Embedding Model**:
|
|
278
|
+
**Embedding Model**: nomic-embed-text-v1.5 via transformers.js v3
|
|
279
|
+
|
|
280
|
+
- Matryoshka Representation Learning (MRL) for flexible dimensions
|
|
281
|
+
- Configurable output: 64, 128, 256, 512, or 768 dimensions
|
|
282
|
+
- Longer context (8192 tokens vs 256 for MiniLM)
|
|
283
|
+
- Better code understanding through specialized training
|
|
284
|
+
- WebGPU support for up to 100x faster inference (when available)
|
|
285
|
+
|
|
286
|
+
**Legacy Model**: all-MiniLM-L6-v2 (fallback)
|
|
233
287
|
|
|
234
|
-
- Fast inference (
|
|
235
|
-
-
|
|
236
|
-
- Good accuracy for code search
|
|
288
|
+
- Fast inference, small footprint (~100MB)
|
|
289
|
+
- Fixed 384-dimensional output
|
|
237
290
|
|
|
238
291
|
**Vector Similarity**: Cosine similarity
|
|
239
292
|
|
package/config.json
CHANGED
|
@@ -60,7 +60,10 @@
|
|
|
60
60
|
"cacheDirectory": "./.smart-coding-cache",
|
|
61
61
|
"watchFiles": false,
|
|
62
62
|
"verbose": false,
|
|
63
|
-
"embeddingModel": "
|
|
63
|
+
"embeddingModel": "nomic-ai/nomic-embed-text-v1.5",
|
|
64
|
+
"embeddingDimension": 256,
|
|
65
|
+
"device": "auto",
|
|
66
|
+
"chunkingMode": "smart",
|
|
64
67
|
"semanticWeight": 0.7,
|
|
65
68
|
"exactMatchBoost": 1.5,
|
|
66
69
|
"workerThreads": "auto"
|
|
@@ -8,14 +8,14 @@ export class VersionChecker {
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates a new VersionChecker instance.
|
|
10
10
|
* @param {Object} config - Configuration object
|
|
11
|
-
* @param {number} [config.versionCheckTimeout=
|
|
11
|
+
* @param {number} [config.versionCheckTimeout=10000] - Timeout for API requests in ms
|
|
12
12
|
* @param {number} [config.versionCacheTTL=300000] - Cache TTL in ms (default: 5 minutes)
|
|
13
13
|
* @param {number} [config.retryAttempts=1] - Number of retry attempts for failed requests
|
|
14
14
|
* @param {number} [config.retryDelay=500] - Delay between retries in ms
|
|
15
15
|
*/
|
|
16
16
|
constructor(config) {
|
|
17
17
|
this.config = config;
|
|
18
|
-
this.timeout = config.versionCheckTimeout ||
|
|
18
|
+
this.timeout = config.versionCheckTimeout || 10000; // 10 seconds for slow APIs
|
|
19
19
|
this.cacheTTL = config.versionCacheTTL || 300000; // 5 minutes
|
|
20
20
|
this.retryAttempts = config.retryAttempts ?? 1;
|
|
21
21
|
this.retryDelay = config.retryDelay || 500;
|
|
@@ -154,7 +154,8 @@ export class VersionChecker {
|
|
|
154
154
|
// Check for Maven patterns last (generic colon for group:artifact or explicit mvn:)
|
|
155
155
|
if (packageName.startsWith("mvn:") || packageName.includes(":")) return "maven";
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
// Default to npm for unprefixed package names (most common use case)
|
|
158
|
+
return "npm";
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
/**
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Status Feature
|
|
3
|
+
*
|
|
4
|
+
* MCP tool to return comprehensive status information about the server.
|
|
5
|
+
* Useful for agents to understand current state and configuration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { createRequire } from 'module';
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const packageJson = require('../package.json');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get tool definition for MCP registration
|
|
17
|
+
*/
|
|
18
|
+
export function getToolDefinition(config) {
|
|
19
|
+
return {
|
|
20
|
+
name: "f_get_status",
|
|
21
|
+
description: "Get comprehensive status information about the Smart Coding MCP server. Returns version, workspace path, model configuration, indexing status, and cache information. Useful for understanding the current state of the semantic search system.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {},
|
|
25
|
+
required: []
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Status Reporter class
|
|
32
|
+
*/
|
|
33
|
+
export class StatusReporter {
|
|
34
|
+
constructor(config, cache, indexer, embedder) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.cache = cache;
|
|
37
|
+
this.indexer = indexer;
|
|
38
|
+
this.embedder = embedder;
|
|
39
|
+
this.startTime = Date.now();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get comprehensive status
|
|
44
|
+
*/
|
|
45
|
+
async getStatus() {
|
|
46
|
+
const vectorStore = this.cache?.getVectorStore() || [];
|
|
47
|
+
|
|
48
|
+
// Get unique files from vector store
|
|
49
|
+
const uniqueFiles = new Set(vectorStore.map(v => v.file));
|
|
50
|
+
|
|
51
|
+
// Get cache size
|
|
52
|
+
let cacheSizeBytes = 0;
|
|
53
|
+
try {
|
|
54
|
+
const cachePath = path.join(this.config.cacheDirectory, 'embeddings.json');
|
|
55
|
+
const stats = await fs.stat(cachePath);
|
|
56
|
+
cacheSizeBytes = stats.size;
|
|
57
|
+
} catch {
|
|
58
|
+
// Cache file doesn't exist yet
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Determine index status
|
|
62
|
+
let indexStatus = 'empty';
|
|
63
|
+
if (this.indexer?.isIndexing) {
|
|
64
|
+
indexStatus = 'indexing';
|
|
65
|
+
} else if (vectorStore.length > 0) {
|
|
66
|
+
indexStatus = 'ready';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
version: packageJson.version,
|
|
71
|
+
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
|
72
|
+
|
|
73
|
+
workspace: {
|
|
74
|
+
path: this.config.searchDirectory,
|
|
75
|
+
cacheDirectory: this.config.cacheDirectory
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
model: {
|
|
79
|
+
name: this.embedder?.modelName || this.config.embeddingModel,
|
|
80
|
+
dimension: this.embedder?.dimension || this.config.embeddingDimension,
|
|
81
|
+
device: this.embedder?.device || this.config.device
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
index: {
|
|
85
|
+
status: indexStatus,
|
|
86
|
+
filesIndexed: uniqueFiles.size,
|
|
87
|
+
chunksCount: vectorStore.length,
|
|
88
|
+
chunkingMode: this.config.chunkingMode
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
cache: {
|
|
92
|
+
enabled: this.config.enableCache,
|
|
93
|
+
path: this.config.cacheDirectory,
|
|
94
|
+
sizeBytes: cacheSizeBytes,
|
|
95
|
+
sizeFormatted: formatBytes(cacheSizeBytes)
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
config: {
|
|
99
|
+
maxResults: this.config.maxResults,
|
|
100
|
+
chunkSize: this.config.chunkSize,
|
|
101
|
+
semanticWeight: this.config.semanticWeight,
|
|
102
|
+
exactMatchBoost: this.config.exactMatchBoost,
|
|
103
|
+
workerThreads: this.config.workerThreads
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Format bytes to human readable
|
|
111
|
+
*/
|
|
112
|
+
function formatBytes(bytes) {
|
|
113
|
+
if (bytes === 0) return '0 B';
|
|
114
|
+
const k = 1024;
|
|
115
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
116
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
117
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Handle MCP tool call
|
|
122
|
+
*/
|
|
123
|
+
export async function handleToolCall(request, instance) {
|
|
124
|
+
const status = await instance.getStatus();
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
content: [{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: JSON.stringify(status, null, 2)
|
|
130
|
+
}]
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -25,6 +25,13 @@ export class CodebaseIndexer {
|
|
|
25
25
|
* Initialize worker thread pool for parallel embedding
|
|
26
26
|
*/
|
|
27
27
|
async initializeWorkers() {
|
|
28
|
+
// Force single-threaded mode for nomic models (transformers.js v3 worker thread issue)
|
|
29
|
+
const isNomicModel = this.config.embeddingModel?.includes('nomic');
|
|
30
|
+
if (isNomicModel) {
|
|
31
|
+
console.error("[Indexer] Single-threaded mode (nomic model - workers disabled for stability)");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
const numWorkers = this.config.workerThreads === "auto"
|
|
29
36
|
? Math.max(1, os.cpus().length - 1)
|
|
30
37
|
: (this.config.workerThreads || 1);
|
|
@@ -48,6 +55,7 @@ export class CodebaseIndexer {
|
|
|
48
55
|
const worker = new Worker(workerPath, {
|
|
49
56
|
workerData: {
|
|
50
57
|
embeddingModel: this.config.embeddingModel,
|
|
58
|
+
embeddingDimension: this.config.embeddingDimension,
|
|
51
59
|
verbose: this.config.verbose
|
|
52
60
|
}
|
|
53
61
|
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set Workspace Feature
|
|
3
|
+
*
|
|
4
|
+
* MCP tool to change the project workspace path at runtime.
|
|
5
|
+
* Useful when agent detects it's in a different directory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get tool definition for MCP registration
|
|
13
|
+
*/
|
|
14
|
+
export function getToolDefinition(config) {
|
|
15
|
+
return {
|
|
16
|
+
name: "e_set_workspace",
|
|
17
|
+
description: "Change the project workspace path at runtime. Use this when you detect the current workspace is incorrect or you need to switch to a different project directory. Creates cache folder automatically and optionally re-indexes the new workspace.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
path: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Absolute path to the new workspace directory"
|
|
24
|
+
},
|
|
25
|
+
clearCache: {
|
|
26
|
+
type: "boolean",
|
|
27
|
+
description: "Whether to clear existing cache before switching (default: false)"
|
|
28
|
+
},
|
|
29
|
+
reindex: {
|
|
30
|
+
type: "boolean",
|
|
31
|
+
description: "Whether to trigger re-indexing after switching (default: true)"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
required: ["path"]
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Workspace Manager class
|
|
41
|
+
*/
|
|
42
|
+
export class WorkspaceManager {
|
|
43
|
+
constructor(config, cache, indexer) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.cache = cache;
|
|
46
|
+
this.indexer = indexer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set new workspace path
|
|
51
|
+
*/
|
|
52
|
+
async setWorkspace(newPath, options = {}) {
|
|
53
|
+
const { clearCache = false, reindex = true } = options;
|
|
54
|
+
|
|
55
|
+
// Validate path
|
|
56
|
+
try {
|
|
57
|
+
const stats = await fs.stat(newPath);
|
|
58
|
+
if (!stats.isDirectory()) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: `Path is not a directory: ${newPath}`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: `Path does not exist: ${newPath}`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const oldPath = this.config.searchDirectory;
|
|
72
|
+
|
|
73
|
+
// Update config
|
|
74
|
+
this.config.searchDirectory = newPath;
|
|
75
|
+
|
|
76
|
+
// Update cache directory
|
|
77
|
+
const newCacheDir = path.join(newPath, '.smart-coding-cache');
|
|
78
|
+
this.config.cacheDirectory = newCacheDir;
|
|
79
|
+
|
|
80
|
+
// Ensure cache directory exists
|
|
81
|
+
try {
|
|
82
|
+
await fs.mkdir(newCacheDir, { recursive: true });
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// Ignore if already exists
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Clear cache if requested
|
|
88
|
+
if (clearCache && this.cache) {
|
|
89
|
+
this.cache.setVectorStore([]);
|
|
90
|
+
this.cache.fileHashes = new Map();
|
|
91
|
+
console.error(`[Workspace] Cache cleared for new workspace`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Update cache path and reload
|
|
95
|
+
if (this.cache) {
|
|
96
|
+
this.cache.config = this.config;
|
|
97
|
+
await this.cache.load();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Update indexer config
|
|
101
|
+
if (this.indexer) {
|
|
102
|
+
this.indexer.config = this.config;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.error(`[Workspace] Changed from ${oldPath} to ${newPath}`);
|
|
106
|
+
|
|
107
|
+
// Trigger re-indexing if requested
|
|
108
|
+
let indexResult = null;
|
|
109
|
+
if (reindex && this.indexer) {
|
|
110
|
+
console.error(`[Workspace] Starting re-indexing...`);
|
|
111
|
+
try {
|
|
112
|
+
indexResult = await this.indexer.indexAll(clearCache);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error(`[Workspace] Re-indexing error: ${err.message}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
oldPath,
|
|
121
|
+
newPath,
|
|
122
|
+
cacheDirectory: newCacheDir,
|
|
123
|
+
reindexed: reindex,
|
|
124
|
+
indexResult
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Handle MCP tool call
|
|
131
|
+
*/
|
|
132
|
+
export async function handleToolCall(request, instance) {
|
|
133
|
+
const { path: newPath, clearCache, reindex } = request.params.arguments || {};
|
|
134
|
+
|
|
135
|
+
if (!newPath) {
|
|
136
|
+
return {
|
|
137
|
+
content: [{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: JSON.stringify({
|
|
140
|
+
success: false,
|
|
141
|
+
error: "Missing required parameter: path"
|
|
142
|
+
}, null, 2)
|
|
143
|
+
}]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const result = await instance.setWorkspace(newPath, { clearCache, reindex });
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
content: [{
|
|
151
|
+
type: "text",
|
|
152
|
+
text: JSON.stringify(result, null, 2)
|
|
153
|
+
}]
|
|
154
|
+
};
|
|
155
|
+
}
|
package/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
import { pipeline } from "@xenova/transformers";
|
|
6
5
|
import fs from "fs/promises";
|
|
7
6
|
import { createRequire } from "module";
|
|
8
7
|
|
|
@@ -12,6 +11,7 @@ const packageJson = require("./package.json");
|
|
|
12
11
|
|
|
13
12
|
import { loadConfig } from "./lib/config.js";
|
|
14
13
|
import { EmbeddingsCache } from "./lib/cache.js";
|
|
14
|
+
import { createEmbedder } from "./lib/mrl-embedder.js";
|
|
15
15
|
import { CodebaseIndexer } from "./features/index-codebase.js";
|
|
16
16
|
import { HybridSearch } from "./features/hybrid-search.js";
|
|
17
17
|
|
|
@@ -19,6 +19,8 @@ import * as IndexCodebaseFeature from "./features/index-codebase.js";
|
|
|
19
19
|
import * as HybridSearchFeature from "./features/hybrid-search.js";
|
|
20
20
|
import * as ClearCacheFeature from "./features/clear-cache.js";
|
|
21
21
|
import * as CheckLastVersionFeature from "./features/check-last-version.js";
|
|
22
|
+
import * as SetWorkspaceFeature from "./features/set-workspace.js";
|
|
23
|
+
import * as GetStatusFeature from "./features/get-status.js";
|
|
22
24
|
|
|
23
25
|
// Parse workspace from command line arguments
|
|
24
26
|
const args = process.argv.slice(2);
|
|
@@ -78,6 +80,16 @@ const features = [
|
|
|
78
80
|
module: CheckLastVersionFeature,
|
|
79
81
|
instance: null,
|
|
80
82
|
handler: CheckLastVersionFeature.handleToolCall
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
module: SetWorkspaceFeature,
|
|
86
|
+
instance: null,
|
|
87
|
+
handler: SetWorkspaceFeature.handleToolCall
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
module: GetStatusFeature,
|
|
91
|
+
instance: null,
|
|
92
|
+
handler: GetStatusFeature.handleToolCall
|
|
81
93
|
}
|
|
82
94
|
];
|
|
83
95
|
|
|
@@ -94,9 +106,10 @@ async function initialize() {
|
|
|
94
106
|
process.exit(1);
|
|
95
107
|
}
|
|
96
108
|
|
|
97
|
-
// Load AI model
|
|
109
|
+
// Load AI model using MRL embedder factory
|
|
98
110
|
console.error("[Server] Loading AI embedding model (this may take time on first run)...");
|
|
99
|
-
embedder = await
|
|
111
|
+
embedder = await createEmbedder(config);
|
|
112
|
+
console.error(`[Server] Model: ${embedder.modelName} (${embedder.dimension}d, device: ${embedder.device})`);
|
|
100
113
|
|
|
101
114
|
// Initialize cache
|
|
102
115
|
cache = new EmbeddingsCache(config);
|
|
@@ -113,6 +126,12 @@ async function initialize() {
|
|
|
113
126
|
features[1].instance = indexer;
|
|
114
127
|
features[2].instance = cacheClearer;
|
|
115
128
|
features[3].instance = versionChecker;
|
|
129
|
+
|
|
130
|
+
// Initialize new tools
|
|
131
|
+
const workspaceManager = new SetWorkspaceFeature.WorkspaceManager(config, cache, indexer);
|
|
132
|
+
const statusReporter = new GetStatusFeature.StatusReporter(config, cache, indexer, embedder);
|
|
133
|
+
features[4].instance = workspaceManager;
|
|
134
|
+
features[5].instance = statusReporter;
|
|
116
135
|
|
|
117
136
|
// Start indexing in background (non-blocking)
|
|
118
137
|
console.error("[Server] Starting background indexing...");
|