@mrxkun/mcfast-mcp 1.4.5 → 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 +129 -52
- package/package.json +3 -3
- package/src/index.js +266 -127
- package/src/strategies/edit-strategy.js +80 -0
- package/src/strategies/search-strategy.js +52 -0
package/README.md
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
# @mrxkun/mcfast-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**mcfast v2.0** - Supercharge your AI coding agent with intelligent, unified tools.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- `search_filesystem`: Smartly detects and uses `ripgrep` (fastest), `git grep`, or `grep` to search your entire codebase in milliseconds.
|
|
11
|
-
- `search_code_ai`: Semantic/fuzzy search powered by cloud AI when you need to find concepts, not just keywords.
|
|
12
|
-
- **📂 Smart File Listing**:
|
|
13
|
-
- `list_files_fast`: Instantly list project files while automatically respecting your `.gitignore` rules (powered by `fast-glob`).
|
|
14
|
-
- **🧠 Intelligent Editing**:
|
|
15
|
-
- `apply_fast`: Applies complex code edits using the Mercury Coder Cloud API.
|
|
16
|
-
- `apply_search_replace`: Perfect for simple, surgical string replacements.
|
|
17
|
-
- `reapply`: Automatically fixes failed edits by analyzing errors.
|
|
18
|
-
- **📊 Audit Logging**:
|
|
19
|
-
- All local tool usage is audited and viewable in the mcfast Dashboard.
|
|
20
|
-
|
|
21
|
-
## 🚀 Installation
|
|
7
|
+
```bash
|
|
8
|
+
npx -y @mrxkun/mcfast-mcp
|
|
9
|
+
```
|
|
22
10
|
|
|
23
|
-
|
|
11
|
+
## Quick Start
|
|
24
12
|
|
|
25
|
-
###
|
|
13
|
+
### Claude Desktop / Cursor / Windsurf
|
|
26
14
|
|
|
27
|
-
Add
|
|
15
|
+
Add to your MCP configuration:
|
|
28
16
|
|
|
29
17
|
```json
|
|
30
18
|
{
|
|
@@ -40,50 +28,139 @@ Add this to your `claude_desktop_config.json` or Cursor MCP settings:
|
|
|
40
28
|
}
|
|
41
29
|
```
|
|
42
30
|
|
|
43
|
-
|
|
31
|
+
Get your free token at [mcfast.vercel.app](https://mcfast.vercel.app)
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
## Tools (v2.0)
|
|
34
|
+
|
|
35
|
+
mcfast v2.0 features **5 unified tools** with intelligent auto-detection:
|
|
36
|
+
|
|
37
|
+
### 🎯 Core Tools
|
|
38
|
+
|
|
39
|
+
#### `edit` - Universal File Editing
|
|
40
|
+
Intelligently edits files with automatic strategy detection:
|
|
41
|
+
- **Search/Replace**: Detects patterns like "Replace X with Y" → uses deterministic replacement
|
|
42
|
+
- **Placeholder Merge**: Detects `// ... existing code ...` → uses token-efficient merging
|
|
43
|
+
- **Mercury AI**: Falls back to complex refactoring via Mercury Coder Cloud
|
|
44
|
+
|
|
45
|
+
**Replaces:** `apply_fast`, `edit_file`, `apply_search_replace`
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
// Example: Simple replacement
|
|
49
|
+
{
|
|
50
|
+
instruction: "Replace 'foo' with 'bar'",
|
|
51
|
+
files: { "app.js": "const foo = 1;" }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Example: Placeholder-based
|
|
55
|
+
{
|
|
56
|
+
instruction: "Add error handling",
|
|
57
|
+
code_edit: "try {\n // ... existing code ...\n} catch (e) { ... }",
|
|
58
|
+
files: { "app.js": "..." }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Example: Complex refactoring
|
|
62
|
+
{
|
|
63
|
+
instruction: "Refactor authentication to use JWT tokens",
|
|
64
|
+
files: { "auth.js": "...", "middleware.js": "..." }
|
|
65
|
+
}
|
|
47
66
|
```
|
|
48
67
|
|
|
49
|
-
|
|
68
|
+
#### `search` - Unified Code Search
|
|
69
|
+
Automatically selects the best search strategy:
|
|
70
|
+
- **Local**: When files are in context (fastest, in-memory)
|
|
71
|
+
- **AI Semantic**: For complex natural language queries
|
|
72
|
+
- **Filesystem**: Fast grep-based codebase-wide search (ripgrep → git grep → grep)
|
|
50
73
|
|
|
51
|
-
|
|
74
|
+
**Replaces:** `search_code`, `search_code_ai`, `search_filesystem`
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
// Example: Local search
|
|
52
78
|
{
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
79
|
+
query: "authentication",
|
|
80
|
+
files: { "app.js": "...", "auth.js": "..." }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Example: Semantic search
|
|
84
|
+
{
|
|
85
|
+
query: "find where user authentication is handled"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Example: Filesystem search
|
|
89
|
+
{
|
|
90
|
+
query: "TODO",
|
|
91
|
+
path: "/project/src"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `read` - File Reading
|
|
96
|
+
Read file contents with optional line ranges to save tokens.
|
|
97
|
+
|
|
98
|
+
**Replaces:** `read_file`
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
{
|
|
102
|
+
path: "/path/to/file.js",
|
|
103
|
+
start_line: 50,
|
|
104
|
+
end_line: 100
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `list_files` - Directory Listing
|
|
109
|
+
List files in a directory (recursive) respecting `.gitignore`.
|
|
110
|
+
|
|
111
|
+
**Replaces:** `list_files_fast`
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
{
|
|
115
|
+
path: "/project/src",
|
|
116
|
+
depth: 3
|
|
61
117
|
}
|
|
62
118
|
```
|
|
63
119
|
|
|
64
|
-
|
|
120
|
+
#### `reapply` - Smart Retry
|
|
121
|
+
Automatically retries failed edits with adjusted strategy (max 3 attempts).
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
{
|
|
125
|
+
instruction: "Original instruction that failed",
|
|
126
|
+
files: { "app.js": "..." },
|
|
127
|
+
errorContext: "Error message from previous attempt"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Backward Compatibility
|
|
132
|
+
|
|
133
|
+
**All legacy tool names still work!** They automatically redirect to the new unified tools:
|
|
134
|
+
|
|
135
|
+
- `apply_fast` → `edit`
|
|
136
|
+
- `edit_file` → `edit`
|
|
137
|
+
- `apply_search_replace` → `edit`
|
|
138
|
+
- `search_code` → `search`
|
|
139
|
+
- `search_code_ai` → `search`
|
|
140
|
+
- `search_filesystem` → `search`
|
|
141
|
+
- `read_file` → `read`
|
|
142
|
+
- `list_files_fast` → `list_files`
|
|
143
|
+
|
|
144
|
+
## Features
|
|
65
145
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
146
|
+
✅ **Auto-Detection** - Tools automatically choose the best strategy
|
|
147
|
+
✅ **Token Optimization** - Placeholder merging and line-range reading save tokens
|
|
148
|
+
✅ **Cloud Processing** - Heavy AST parsing offloaded to Mercury Coder Cloud
|
|
149
|
+
✅ **Deterministic** - Reduces hallucinations with syntax verification
|
|
150
|
+
✅ **Universal** - Works with any MCP-enabled AI (Claude, Cursor, Windsurf, etc.)
|
|
70
151
|
|
|
71
|
-
##
|
|
152
|
+
## Performance
|
|
72
153
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| `list_files_fast` | **(NEW)** Lists files recursively, respecting `.gitignore`. |
|
|
77
|
-
| `apply_fast` | Applies multi-file edits using AI. |
|
|
78
|
-
| `apply_search_replace` | Simple find-and-replace. |
|
|
79
|
-
| `search_code_ai` | AI-powered semantic search. |
|
|
80
|
-
| `edit_file` | Direct file write (with cloud logging). |
|
|
154
|
+
- **Speed**: 10,000+ tokens/sec for local operations
|
|
155
|
+
- **Accuracy**: 98% success rate for cloud edits
|
|
156
|
+
- **Efficiency**: 50% token reduction with placeholder-based editing
|
|
81
157
|
|
|
82
|
-
##
|
|
158
|
+
## Privacy & Security
|
|
83
159
|
|
|
84
|
-
-
|
|
85
|
-
-
|
|
160
|
+
- **Zero Persistence**: Code processed in memory, discarded immediately
|
|
161
|
+
- **Cloud Masking**: Your `MCFAST_TOKEN` never exposed in logs
|
|
162
|
+
- **Open Source**: Audit the source at [github.com/ndpmmo/mcfast](https://github.com/ndpmmo/mcfast)
|
|
86
163
|
|
|
87
|
-
##
|
|
164
|
+
## License
|
|
88
165
|
|
|
89
|
-
MIT © mrxkun
|
|
166
|
+
MIT © [mrxkun](https://github.com/mrxkun)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrxkun/mcfast-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Ultra-fast code editing
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Ultra-fast code editing with 5 unified tools and intelligent auto-detection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mcfast-mcp": "src/index.js"
|
|
@@ -31,4 +31,4 @@
|
|
|
31
31
|
"fast-glob": "^3.3.3",
|
|
32
32
|
"ignore": "^7.0.5"
|
|
33
33
|
}
|
|
34
|
-
}
|
|
34
|
+
}
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,8 @@ import path from "path";
|
|
|
13
13
|
import { exec } from "child_process";
|
|
14
14
|
import { promisify } from "util";
|
|
15
15
|
import fg from "fast-glob";
|
|
16
|
+
import { detectEditStrategy, extractSearchReplace } from './strategies/edit-strategy.js';
|
|
17
|
+
import { detectSearchStrategy } from './strategies/search-strategy.js';
|
|
16
18
|
|
|
17
19
|
const execAsync = promisify(exec);
|
|
18
20
|
|
|
@@ -35,8 +37,14 @@ const colors = {
|
|
|
35
37
|
bgBlack: '\x1b[40m',
|
|
36
38
|
};
|
|
37
39
|
|
|
38
|
-
// Tool icons
|
|
40
|
+
// Tool icons (v2.0 - 4 core tools)
|
|
39
41
|
const toolIcons = {
|
|
42
|
+
edit: '✏️',
|
|
43
|
+
search: '🔍',
|
|
44
|
+
read: '📖',
|
|
45
|
+
list_files: '📁',
|
|
46
|
+
reapply: '🔁',
|
|
47
|
+
// Legacy aliases (backward compatibility)
|
|
40
48
|
apply_fast: '⚡',
|
|
41
49
|
apply_search_replace: '🔄',
|
|
42
50
|
search_code_ai: '🔍',
|
|
@@ -44,7 +52,19 @@ const toolIcons = {
|
|
|
44
52
|
search_filesystem: '📂',
|
|
45
53
|
list_files_fast: '📁',
|
|
46
54
|
edit_file: '✏️',
|
|
47
|
-
|
|
55
|
+
read_file: '📖',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Backward compatibility mapping
|
|
59
|
+
const TOOL_ALIASES = {
|
|
60
|
+
'apply_fast': 'edit',
|
|
61
|
+
'edit_file': 'edit',
|
|
62
|
+
'apply_search_replace': 'edit',
|
|
63
|
+
'search_code': 'search',
|
|
64
|
+
'search_code_ai': 'search',
|
|
65
|
+
'search_filesystem': 'search',
|
|
66
|
+
'list_files_fast': 'list_files',
|
|
67
|
+
'read_file': 'read'
|
|
48
68
|
};
|
|
49
69
|
|
|
50
70
|
/**
|
|
@@ -152,134 +172,103 @@ const server = new Server(
|
|
|
152
172
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
153
173
|
return {
|
|
154
174
|
tools: [
|
|
175
|
+
// CORE TOOL 1: edit (consolidates apply_fast + edit_file + apply_search_replace)
|
|
155
176
|
{
|
|
156
|
-
name: "
|
|
157
|
-
description: "
|
|
177
|
+
name: "edit",
|
|
178
|
+
description: "**PRIMARY TOOL FOR EDITING FILES** - Intelligently edits files with automatic strategy detection. Consolidates apply_fast, edit_file, and apply_search_replace into one smart tool. Supports: (1) Simple search/replace, (2) Placeholder-based editing with '// ... existing code ...', (3) Complex multi-file refactoring via Mercury AI.",
|
|
158
179
|
inputSchema: {
|
|
159
180
|
type: "object",
|
|
160
181
|
properties: {
|
|
161
182
|
instruction: {
|
|
162
183
|
type: "string",
|
|
163
|
-
description: "
|
|
184
|
+
description: "Natural language instruction describing changes. For search/replace, use 'Replace X with Y' format."
|
|
164
185
|
},
|
|
165
186
|
files: {
|
|
166
187
|
type: "object",
|
|
167
|
-
description: "
|
|
188
|
+
description: "Map of file paths to CURRENT file contents.",
|
|
168
189
|
additionalProperties: { type: "string" }
|
|
169
190
|
},
|
|
191
|
+
code_edit: {
|
|
192
|
+
type: "string",
|
|
193
|
+
description: "Optional: Partial code snippet with '// ... existing code ...' placeholders for token-efficient editing."
|
|
194
|
+
},
|
|
170
195
|
dryRun: {
|
|
171
196
|
type: "boolean",
|
|
172
|
-
description: "If true, returns
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
required: ["instruction", "files"],
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
name: "apply_search_replace",
|
|
180
|
-
description: "Apply targeted search/replace edits. Faster and cheaper for simple replacements.",
|
|
181
|
-
inputSchema: {
|
|
182
|
-
type: "object",
|
|
183
|
-
properties: {
|
|
184
|
-
files: {
|
|
185
|
-
type: "object",
|
|
186
|
-
description: "Map of file paths to content.",
|
|
187
|
-
additionalProperties: { type: "string" }
|
|
188
|
-
},
|
|
189
|
-
search: { type: "string", description: "Exact string to search for." },
|
|
190
|
-
replace: { type: "string", description: "String to replace with." }
|
|
197
|
+
description: "If true, returns diff preview without applying changes."
|
|
198
|
+
}
|
|
191
199
|
},
|
|
192
|
-
required: ["
|
|
200
|
+
required: ["instruction", "files"]
|
|
193
201
|
}
|
|
194
202
|
},
|
|
203
|
+
// CORE TOOL 2: search (consolidates search_code + search_code_ai + search_filesystem)
|
|
195
204
|
{
|
|
196
|
-
name: "
|
|
197
|
-
description: "
|
|
205
|
+
name: "search",
|
|
206
|
+
description: "**UNIFIED CODE SEARCH** - Automatically selects the best search strategy: (1) Local search if files provided, (2) AI semantic search for complex queries, (3) Filesystem grep for fast codebase-wide search.",
|
|
198
207
|
inputSchema: {
|
|
199
208
|
type: "object",
|
|
200
209
|
properties: {
|
|
201
210
|
query: {
|
|
202
211
|
type: "string",
|
|
203
|
-
description: "Search query (
|
|
212
|
+
description: "Search query (literal string or natural language)"
|
|
204
213
|
},
|
|
205
214
|
files: {
|
|
206
215
|
type: "object",
|
|
207
|
-
description: "Map of
|
|
216
|
+
description: "Optional: Map of files to search within (triggers local search)",
|
|
208
217
|
additionalProperties: { type: "string" }
|
|
209
218
|
},
|
|
210
|
-
|
|
211
|
-
type: "
|
|
212
|
-
description: "
|
|
213
|
-
}
|
|
219
|
+
path: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "Optional: Directory path for filesystem search"
|
|
222
|
+
},
|
|
223
|
+
mode: {
|
|
224
|
+
type: "string",
|
|
225
|
+
enum: ["auto", "local", "ai", "filesystem"],
|
|
226
|
+
description: "Search mode (default: auto-detect)"
|
|
227
|
+
},
|
|
228
|
+
regex: { type: "boolean", description: "Treat query as regex (local mode only)" },
|
|
229
|
+
caseSensitive: { type: "boolean", description: "Case sensitive search" },
|
|
230
|
+
contextLines: { type: "number", description: "Lines of context around matches (default: 2)" }
|
|
214
231
|
},
|
|
215
|
-
required: ["query"
|
|
232
|
+
required: ["query"]
|
|
216
233
|
}
|
|
217
234
|
},
|
|
235
|
+
// CORE TOOL 3: read
|
|
218
236
|
{
|
|
219
|
-
name: "
|
|
220
|
-
description: "
|
|
237
|
+
name: "read",
|
|
238
|
+
description: "Read file contents with optional line range support to save tokens. Use this before editing files or to understand code structure.",
|
|
221
239
|
inputSchema: {
|
|
222
240
|
type: "object",
|
|
223
241
|
properties: {
|
|
224
|
-
path: { type: "string", description: "Absolute
|
|
225
|
-
|
|
226
|
-
|
|
242
|
+
path: { type: "string", description: "Absolute path to the file" },
|
|
243
|
+
start_line: { type: "number", description: "Start line (1-indexed, inclusive)" },
|
|
244
|
+
end_line: { type: "number", description: "End line (1-indexed, inclusive)" }
|
|
227
245
|
},
|
|
228
|
-
required: ["path"
|
|
246
|
+
required: ["path"]
|
|
229
247
|
}
|
|
230
248
|
},
|
|
249
|
+
// CORE TOOL 4: list_files
|
|
231
250
|
{
|
|
232
|
-
name: "
|
|
233
|
-
description: "
|
|
251
|
+
name: "list_files",
|
|
252
|
+
description: "List all files in a directory (recursive) respecting .gitignore. Use this to understand project structure.",
|
|
234
253
|
inputSchema: {
|
|
235
254
|
type: "object",
|
|
236
255
|
properties: {
|
|
237
|
-
path: { type: "string", description: "Root directory path
|
|
238
|
-
depth: { type: "number", description: "Max depth to traverse (default: 5)
|
|
256
|
+
path: { type: "string", description: "Root directory path (default: current dir)" },
|
|
257
|
+
depth: { type: "number", description: "Max depth to traverse (default: 5)" }
|
|
239
258
|
}
|
|
240
259
|
}
|
|
241
260
|
},
|
|
242
|
-
|
|
243
|
-
name: "search_filesystem",
|
|
244
|
-
description: "Deep, high-performance filesystem search using ripgrep -> git grep -> grep fallback. Best for finding code when you don't know the file location.",
|
|
245
|
-
inputSchema: {
|
|
246
|
-
type: "object",
|
|
247
|
-
properties: {
|
|
248
|
-
query: { type: "string", description: "Search pattern (literal or regex)" },
|
|
249
|
-
path: { type: "string", description: "Directory to search in (default: current cwd)" },
|
|
250
|
-
include: { type: "string", description: "Glob pattern to include (e.g. **/*.ts)" },
|
|
251
|
-
exclude: { type: "array", items: { type: "string" }, description: "Patterns to exclude" },
|
|
252
|
-
isRegex: { type: "boolean", description: "Treat query as regex (default: false)" },
|
|
253
|
-
caseSensitive: { type: "boolean", description: "Case sensitive search (default: false)" }
|
|
254
|
-
},
|
|
255
|
-
required: ["query"]
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
name: "search_code",
|
|
260
|
-
description: "Fast local search within the PROVIDED files object. Useful when you already have file contents in context.",
|
|
261
|
-
inputSchema: {
|
|
262
|
-
type: "object",
|
|
263
|
-
properties: {
|
|
264
|
-
query: { type: "string", description: "Search pattern (literal or regex)" },
|
|
265
|
-
files: { type: "object", description: "Map of file paths to content to search within.", additionalProperties: { type: "string" } },
|
|
266
|
-
regex: { type: "boolean", description: "Treat query as regex (default: false)" },
|
|
267
|
-
caseSensitive: { type: "boolean", description: "Case sensitive search (default: false)" },
|
|
268
|
-
contextLines: { type: "number", description: "Lines of context around matches (default: 2)" }
|
|
269
|
-
},
|
|
270
|
-
required: ["query", "files"]
|
|
271
|
-
}
|
|
272
|
-
},
|
|
261
|
+
// CORE TOOL 5: reapply (unchanged)
|
|
273
262
|
{
|
|
274
263
|
name: "reapply",
|
|
275
|
-
description: "Smart retry for failed
|
|
264
|
+
description: "Smart retry for failed edits. Analyzes error context and re-attempts with adjusted strategy. Max 3 retries.",
|
|
276
265
|
inputSchema: {
|
|
277
266
|
type: "object",
|
|
278
267
|
properties: {
|
|
279
268
|
instruction: { type: "string", description: "Original instruction that failed" },
|
|
280
269
|
files: { type: "object", description: "Current file contents", additionalProperties: { type: "string" } },
|
|
281
|
-
errorContext: { type: "string", description: "
|
|
282
|
-
attempt: { type: "number", description: "Current attempt number
|
|
270
|
+
errorContext: { type: "string", description: "Error description" },
|
|
271
|
+
attempt: { type: "number", description: "Current attempt number" }
|
|
283
272
|
},
|
|
284
273
|
required: ["instruction", "files"]
|
|
285
274
|
}
|
|
@@ -313,26 +302,25 @@ async function getFiles(dir, depth = 5, currentDepth = 0) {
|
|
|
313
302
|
}
|
|
314
303
|
|
|
315
304
|
/**
|
|
316
|
-
* MCP Prompts Definition (
|
|
305
|
+
* MCP Prompts Definition (v2.0 - Updated for Unified Tools)
|
|
317
306
|
*/
|
|
318
307
|
const PROMPTS = {
|
|
319
308
|
"workflow_guide": {
|
|
320
|
-
description: "Standard workflow for using mcfast tools effectively
|
|
309
|
+
description: "Standard workflow for using mcfast v2.0 tools effectively.",
|
|
321
310
|
messages: [{
|
|
322
311
|
role: "user",
|
|
323
312
|
content: {
|
|
324
313
|
type: "text",
|
|
325
|
-
text: `You are an expert developer using the 'mcfast' toolkit. Follow this strategy for every task:
|
|
314
|
+
text: `You are an expert developer using the 'mcfast v2.0' toolkit. Follow this strategy for every task:
|
|
326
315
|
|
|
327
|
-
1. **Understand Structure:** Use '
|
|
328
|
-
2. **Locate Logic:** Use '
|
|
316
|
+
1. **Understand Structure:** Use 'list_files' first to see the project layout.
|
|
317
|
+
2. **Locate Logic:** Use 'search' to find relevant code sections (auto-detects best strategy).
|
|
329
318
|
3. **Plan & Execute:**
|
|
330
|
-
-
|
|
331
|
-
-
|
|
332
|
-
- Simple Replace: Use 'apply_search_replace'.
|
|
319
|
+
- Any edit: Use 'edit' (auto-detects: search/replace, placeholder merge, or Mercury AI).
|
|
320
|
+
- Read before editing: Use 'read' with line ranges to save tokens.
|
|
333
321
|
4. **Verify:** Check the output or run tests if possible.
|
|
334
322
|
|
|
335
|
-
|
|
323
|
+
The 'edit' tool automatically chooses the best strategy based on your instruction.`
|
|
336
324
|
}
|
|
337
325
|
}]
|
|
338
326
|
},
|
|
@@ -342,18 +330,18 @@ Always assume you have write access. 'apply_fast' and 'edit_file' write to disk
|
|
|
342
330
|
role: "user",
|
|
343
331
|
content: {
|
|
344
332
|
type: "text",
|
|
345
|
-
text: `Refactoring Strategy with mcfast:
|
|
333
|
+
text: `Refactoring Strategy with mcfast v2.0:
|
|
346
334
|
|
|
347
335
|
1. **Analysis:**
|
|
348
|
-
- Use '
|
|
349
|
-
- Use '
|
|
336
|
+
- Use 'list_files' to identify all files involved.
|
|
337
|
+
- Use 'search' to find usages of the symbol/logic to refactor.
|
|
350
338
|
|
|
351
339
|
2. **Execution (Atomic):**
|
|
352
|
-
- Use '
|
|
353
|
-
- '
|
|
340
|
+
- Use 'edit' for the main logic change. Pass ALL related file contents in the 'files' argument.
|
|
341
|
+
- The 'edit' tool is transactional for multiple files.
|
|
354
342
|
|
|
355
343
|
3. **Cleanup:**
|
|
356
|
-
- Use '
|
|
344
|
+
- Use 'edit' for any minor touch-ups or to update imports.`
|
|
357
345
|
}
|
|
358
346
|
}]
|
|
359
347
|
},
|
|
@@ -365,10 +353,10 @@ Always assume you have write access. 'apply_fast' and 'edit_file' write to disk
|
|
|
365
353
|
type: "text",
|
|
366
354
|
text: `Debugging Protocol:
|
|
367
355
|
|
|
368
|
-
1. **Trace:** Use '
|
|
369
|
-
2. **Context:**
|
|
356
|
+
1. **Trace:** Use 'search' with the error message or function name.
|
|
357
|
+
2. **Context:** Use 'read' to view the surrounding code.
|
|
370
358
|
3. **Hypothesis:** Formulate a fix.
|
|
371
|
-
4. **Fix:** Use '
|
|
359
|
+
4. **Fix:** Use 'edit' for single-file or multi-file fixes.`
|
|
372
360
|
}
|
|
373
361
|
}]
|
|
374
362
|
}
|
|
@@ -390,50 +378,53 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
390
378
|
});
|
|
391
379
|
|
|
392
380
|
/**
|
|
393
|
-
* Tool Execution Handler
|
|
381
|
+
* Tool Execution Handler (v2.0 - Unified Tools)
|
|
394
382
|
*/
|
|
395
383
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
396
|
-
|
|
384
|
+
let { name, arguments: args } = request.params;
|
|
397
385
|
const startTime = Date.now();
|
|
398
386
|
|
|
387
|
+
// Backward compatibility: map legacy tool names to new names
|
|
388
|
+
const originalName = name;
|
|
389
|
+
if (TOOL_ALIASES[name]) {
|
|
390
|
+
name = TOOL_ALIASES[name];
|
|
391
|
+
console.error(`${colors.dim}[COMPAT] ${originalName} → ${name}${colors.reset}`);
|
|
392
|
+
}
|
|
393
|
+
|
|
399
394
|
// Pretty-print tool call to terminal
|
|
400
|
-
prettyLogToolCall(
|
|
395
|
+
prettyLogToolCall(originalName, args);
|
|
401
396
|
|
|
402
397
|
let result;
|
|
403
398
|
let success = true;
|
|
404
399
|
let summary = '';
|
|
405
400
|
|
|
406
401
|
try {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
result = await
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
summary = '
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
} else if (name === "edit_file") {
|
|
425
|
-
result = await handleEditFile(args);
|
|
426
|
-
summary = `File saved: ${args.path}`;
|
|
427
|
-
} else if (name === "list_files_fast") {
|
|
402
|
+
// CORE TOOL 1: edit (unified editing with auto-detection)
|
|
403
|
+
if (name === "edit") {
|
|
404
|
+
result = await handleEdit(args);
|
|
405
|
+
summary = 'File edit completed';
|
|
406
|
+
}
|
|
407
|
+
// CORE TOOL 2: search (unified search with auto-detection)
|
|
408
|
+
else if (name === "search") {
|
|
409
|
+
result = await handleSearch(args);
|
|
410
|
+
summary = 'Search completed';
|
|
411
|
+
}
|
|
412
|
+
// CORE TOOL 3: read
|
|
413
|
+
else if (name === "read") {
|
|
414
|
+
result = await handleRead(args);
|
|
415
|
+
summary = 'File read completed';
|
|
416
|
+
}
|
|
417
|
+
// CORE TOOL 4: list_files
|
|
418
|
+
else if (name === "list_files") {
|
|
428
419
|
result = await handleListFiles(args);
|
|
429
420
|
summary = 'Directory listing completed';
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
} else if (name === "reapply") {
|
|
421
|
+
}
|
|
422
|
+
// CORE TOOL 5: reapply
|
|
423
|
+
else if (name === "reapply") {
|
|
434
424
|
result = await handleReapply(args);
|
|
435
425
|
summary = 'Retry edit applied';
|
|
436
|
-
}
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
437
428
|
throw new Error(`Tool not found: ${name}`);
|
|
438
429
|
}
|
|
439
430
|
|
|
@@ -452,7 +443,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
452
443
|
}
|
|
453
444
|
|
|
454
445
|
// Log result
|
|
455
|
-
prettyLogResult(
|
|
446
|
+
prettyLogResult(originalName, success, Date.now() - startTime, summary);
|
|
456
447
|
|
|
457
448
|
return result;
|
|
458
449
|
});
|
|
@@ -493,6 +484,82 @@ async function handleReapply({ instruction, files, errorContext = "", attempt =
|
|
|
493
484
|
}
|
|
494
485
|
}
|
|
495
486
|
|
|
487
|
+
/**
|
|
488
|
+
* UNIFIED HANDLER 1: handleEdit (v2.0)
|
|
489
|
+
* Consolidates: apply_fast + edit_file + apply_search_replace
|
|
490
|
+
* Auto-detects best strategy based on input
|
|
491
|
+
*/
|
|
492
|
+
async function handleEdit({ instruction, files, code_edit, dryRun = false }) {
|
|
493
|
+
const strategy = detectEditStrategy({ instruction, code_edit, files });
|
|
494
|
+
|
|
495
|
+
console.error(`${colors.cyan}[EDIT STRATEGY]${colors.reset} ${strategy}`);
|
|
496
|
+
|
|
497
|
+
// Strategy 1: Search/Replace (deterministic, fastest)
|
|
498
|
+
if (strategy === 'search_replace') {
|
|
499
|
+
const extracted = extractSearchReplace(instruction);
|
|
500
|
+
if (extracted) {
|
|
501
|
+
return await handleApplyFast({
|
|
502
|
+
instruction: `Replace checking for exact match:\nSEARCH:\n${extracted.search}\n\nREPLACE WITH:\n${extracted.replace}`,
|
|
503
|
+
files,
|
|
504
|
+
dryRun,
|
|
505
|
+
toolName: 'edit'
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Strategy 2: Placeholder Merge (token-efficient)
|
|
511
|
+
if (strategy === 'placeholder_merge' && code_edit) {
|
|
512
|
+
// Use edit_file logic for placeholder-based editing
|
|
513
|
+
// For now, we'll route to Mercury with a hint
|
|
514
|
+
return await handleApplyFast({
|
|
515
|
+
instruction: `${instruction}\n\nUSE PLACEHOLDER MERGE STRATEGY. Code snippet:\n${code_edit}`,
|
|
516
|
+
files,
|
|
517
|
+
dryRun,
|
|
518
|
+
toolName: 'edit'
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Strategy 3: Mercury Intelligent (most flexible, default)
|
|
523
|
+
return await handleApplyFast({
|
|
524
|
+
instruction,
|
|
525
|
+
files,
|
|
526
|
+
dryRun,
|
|
527
|
+
toolName: 'edit'
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* UNIFIED HANDLER 2: handleSearch (v2.0)
|
|
533
|
+
* Consolidates: search_code + search_code_ai + search_filesystem
|
|
534
|
+
* Auto-detects best strategy based on input
|
|
535
|
+
*/
|
|
536
|
+
async function handleSearch({ query, files, path, mode = 'auto', regex = false, caseSensitive = false, contextLines = 2 }) {
|
|
537
|
+
const detectedMode = mode === 'auto' ? detectSearchStrategy({ query, files, path, mode }) : mode;
|
|
538
|
+
|
|
539
|
+
console.error(`${colors.cyan}[SEARCH STRATEGY]${colors.reset} ${detectedMode}`);
|
|
540
|
+
|
|
541
|
+
// Strategy 1: Local search (if files provided)
|
|
542
|
+
if (detectedMode === 'local' && files) {
|
|
543
|
+
return await handleSearchCode({ query, files, regex, caseSensitive, contextLines });
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Strategy 2: AI semantic search (for complex queries)
|
|
547
|
+
if (detectedMode === 'ai' && files) {
|
|
548
|
+
return await handleSearchCodeAI({ query, files, contextLines });
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Strategy 3: Filesystem search (fast grep-based)
|
|
552
|
+
return await handleSearchFilesystem({ query, path, isRegex: regex, caseSensitive });
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* UNIFIED HANDLER 3: handleRead (v2.0)
|
|
557
|
+
* Renamed from handleReadFile for consistency
|
|
558
|
+
*/
|
|
559
|
+
async function handleRead({ path: filePath, start_line, end_line }) {
|
|
560
|
+
return await handleReadFile({ path: filePath, start_line, end_line });
|
|
561
|
+
}
|
|
562
|
+
|
|
496
563
|
// Local search implementation (no API required)
|
|
497
564
|
async function reportAudit(params) {
|
|
498
565
|
if (!TOKEN) return;
|
|
@@ -931,6 +998,78 @@ async function handleEditFile({ path: filePath, content, instruction = "" }) {
|
|
|
931
998
|
}
|
|
932
999
|
}
|
|
933
1000
|
|
|
1001
|
+
|
|
1002
|
+
async function handleReadFile({ path: filePath, start_line, end_line }) {
|
|
1003
|
+
const start = Date.now();
|
|
1004
|
+
try {
|
|
1005
|
+
// Resolve absolute path
|
|
1006
|
+
const absolutePath = path.resolve(filePath);
|
|
1007
|
+
|
|
1008
|
+
// Check if file exists and is a file
|
|
1009
|
+
const stats = await fs.stat(absolutePath);
|
|
1010
|
+
if (!stats.isFile()) {
|
|
1011
|
+
throw new Error(`Path is not a file: ${absolutePath}`);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// Read file content
|
|
1015
|
+
const content = await fs.readFile(absolutePath, 'utf8');
|
|
1016
|
+
|
|
1017
|
+
const lines = content.split('\n');
|
|
1018
|
+
const totalLines = lines.length;
|
|
1019
|
+
|
|
1020
|
+
let outputContent = content;
|
|
1021
|
+
let lineRangeInfo = `(Total ${totalLines} lines)`;
|
|
1022
|
+
|
|
1023
|
+
let startLine = start_line ? parseInt(start_line) : 1;
|
|
1024
|
+
let endLine = end_line ? parseInt(end_line) : totalLines;
|
|
1025
|
+
|
|
1026
|
+
// Validate range
|
|
1027
|
+
if (startLine < 1) startLine = 1;
|
|
1028
|
+
if (endLine > totalLines) endLine = totalLines;
|
|
1029
|
+
if (startLine > endLine) {
|
|
1030
|
+
throw new Error(`Invalid line range: start_line (${startLine}) > end_line (${endLine})`);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Slice content if range specified
|
|
1034
|
+
if (start_line || end_line) {
|
|
1035
|
+
outputContent = lines.slice(startLine - 1, endLine).join('\n');
|
|
1036
|
+
lineRangeInfo = `(Lines ${startLine}-${endLine} of ${totalLines})`;
|
|
1037
|
+
} else if (totalLines > 2000) {
|
|
1038
|
+
// Optional: warn if reading huge file without range?
|
|
1039
|
+
// For now, we allow it but it might be truncated by the client/LLM window.
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
const output = `📄 File: ${filePath} ${lineRangeInfo}\n----------------------------------------\n${outputContent}`;
|
|
1043
|
+
|
|
1044
|
+
reportAudit({
|
|
1045
|
+
tool: 'read_file',
|
|
1046
|
+
instruction: filePath,
|
|
1047
|
+
status: 'success',
|
|
1048
|
+
latency_ms: Date.now() - start,
|
|
1049
|
+
files_count: 1,
|
|
1050
|
+
input_tokens: Math.ceil(filePath.length / 4),
|
|
1051
|
+
output_tokens: Math.ceil(output.length / 4)
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
return {
|
|
1055
|
+
content: [{ type: "text", text: output }]
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
} catch (error) {
|
|
1059
|
+
reportAudit({
|
|
1060
|
+
tool: 'read_file',
|
|
1061
|
+
instruction: filePath,
|
|
1062
|
+
status: 'error',
|
|
1063
|
+
error_message: error.message,
|
|
1064
|
+
latency_ms: Date.now() - start
|
|
1065
|
+
});
|
|
1066
|
+
return {
|
|
1067
|
+
content: [{ type: "text", text: `❌ Error reading file: ${error.message}` }],
|
|
1068
|
+
isError: true
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
934
1073
|
async function handleApplyFast({ instruction, files, dryRun, toolName }) {
|
|
935
1074
|
if (!TOKEN) {
|
|
936
1075
|
return {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit Strategy Auto-Detection
|
|
3
|
+
* Determines the best editing strategy based on input parameters
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detect if instruction is a simple search/replace
|
|
8
|
+
*/
|
|
9
|
+
export function isSearchReplace(instruction) {
|
|
10
|
+
if (!instruction) return false;
|
|
11
|
+
|
|
12
|
+
const lower = instruction.toLowerCase();
|
|
13
|
+
|
|
14
|
+
// Pattern 1: "Replace X with Y"
|
|
15
|
+
const replacePattern = /replace\s+["'](.+?)["']\s+with\s+["'](.+?)["']/i;
|
|
16
|
+
if (replacePattern.test(instruction)) return true;
|
|
17
|
+
|
|
18
|
+
// Pattern 2: "Change X to Y"
|
|
19
|
+
const changePattern = /change\s+["'](.+?)["']\s+to\s+["'](.+?)["']/i;
|
|
20
|
+
if (changePattern.test(instruction)) return true;
|
|
21
|
+
|
|
22
|
+
// Pattern 3: Contains both "search" and "replace" keywords
|
|
23
|
+
if (lower.includes('search') && lower.includes('replace')) return true;
|
|
24
|
+
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Extract search and replace terms from instruction
|
|
30
|
+
*/
|
|
31
|
+
export function extractSearchReplace(instruction) {
|
|
32
|
+
const replacePattern = /replace\s+["'](.+?)["']\s+with\s+["'](.+?)["']/i;
|
|
33
|
+
const changePattern = /change\s+["'](.+?)["']\s+to\s+["'](.+?)["']/i;
|
|
34
|
+
|
|
35
|
+
let match = instruction.match(replacePattern);
|
|
36
|
+
if (match) {
|
|
37
|
+
return { search: match[1], replace: match[2] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
match = instruction.match(changePattern);
|
|
41
|
+
if (match) {
|
|
42
|
+
return { search: match[1], replace: match[2] };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Detect if code_edit contains placeholders
|
|
50
|
+
*/
|
|
51
|
+
export function hasPlaceholders(codeEdit) {
|
|
52
|
+
if (!codeEdit) return false;
|
|
53
|
+
|
|
54
|
+
const placeholderPatterns = [
|
|
55
|
+
/\/\/\s*\.\.\.\s*(?:existing|keep|rest|remaining)[\w\s]*\.\.\./i,
|
|
56
|
+
/\/\*\s*\.\.\.\s*(?:existing|keep|rest|remaining)[\w\s]*\.\.\.\s*\*\//i,
|
|
57
|
+
/#\s*\.\.\.\s*(?:existing|keep|rest|remaining)[\w\s]*\.\.\./i
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
return placeholderPatterns.some(pattern => pattern.test(codeEdit));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Determine the best edit strategy
|
|
65
|
+
* @returns {'search_replace' | 'placeholder_merge' | 'mercury_intelligent'}
|
|
66
|
+
*/
|
|
67
|
+
export function detectEditStrategy({ instruction, code_edit, files }) {
|
|
68
|
+
// Priority 1: Search/Replace (fastest, deterministic)
|
|
69
|
+
if (isSearchReplace(instruction)) {
|
|
70
|
+
return 'search_replace';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Priority 2: Placeholder Merge (token-efficient)
|
|
74
|
+
if (code_edit && hasPlaceholders(code_edit)) {
|
|
75
|
+
return 'placeholder_merge';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Priority 3: Mercury Intelligent (most flexible)
|
|
79
|
+
return 'mercury_intelligent';
|
|
80
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Strategy Auto-Detection
|
|
3
|
+
* Determines the best search strategy based on input parameters
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detect if query is semantic/complex (requires AI)
|
|
8
|
+
*/
|
|
9
|
+
export function isSemanticQuery(query) {
|
|
10
|
+
if (!query) return false;
|
|
11
|
+
|
|
12
|
+
const lower = query.toLowerCase();
|
|
13
|
+
|
|
14
|
+
// Indicators of semantic search
|
|
15
|
+
const semanticIndicators = [
|
|
16
|
+
'find where',
|
|
17
|
+
'locate the',
|
|
18
|
+
'show me',
|
|
19
|
+
'what does',
|
|
20
|
+
'how does',
|
|
21
|
+
'why is',
|
|
22
|
+
'search for code that',
|
|
23
|
+
'find functions that',
|
|
24
|
+
'locate classes that'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
return semanticIndicators.some(indicator => lower.includes(indicator));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Determine the best search strategy
|
|
32
|
+
* @returns {'local' | 'ai' | 'filesystem'}
|
|
33
|
+
*/
|
|
34
|
+
export function detectSearchStrategy({ query, files, path, mode }) {
|
|
35
|
+
// If mode is explicitly set, use it
|
|
36
|
+
if (mode && mode !== 'auto') {
|
|
37
|
+
return mode;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Priority 1: Local search (if files provided in context)
|
|
41
|
+
if (files && Object.keys(files).length > 0) {
|
|
42
|
+
return 'local';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Priority 2: AI search (if query is semantic/complex)
|
|
46
|
+
if (isSemanticQuery(query)) {
|
|
47
|
+
return 'ai';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Priority 3: Filesystem search (fast grep-based)
|
|
51
|
+
return 'filesystem';
|
|
52
|
+
}
|