mcp-code-context 2.6.0 → 3.0.4
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/CHANGELOG.md +92 -260
- package/README.md +304 -298
- package/dist/src/core/engine.d.ts +21 -0
- package/dist/src/core/engine.js +68 -0
- package/dist/src/core/engine.js.map +1 -0
- package/dist/src/core/validator.d.ts +15 -0
- package/dist/src/core/validator.js +44 -0
- package/dist/src/core/validator.js.map +1 -0
- package/dist/src/index.d.ts +7 -11
- package/dist/src/index.js +182 -397
- package/dist/src/index.js.map +1 -1
- package/dist/src/operations/write.d.ts +51 -0
- package/dist/src/operations/write.js +169 -0
- package/dist/src/operations/write.js.map +1 -0
- package/dist/src/parsers/base.d.ts +28 -0
- package/dist/src/parsers/base.js +27 -0
- package/dist/src/parsers/base.js.map +1 -0
- package/dist/src/parsers/dartTreeSitter.d.ts +13 -0
- package/dist/src/parsers/dartTreeSitter.js +91 -0
- package/dist/src/parsers/dartTreeSitter.js.map +1 -0
- package/dist/src/parsers/php.d.ts +12 -0
- package/dist/src/parsers/php.js +86 -0
- package/dist/src/parsers/php.js.map +1 -0
- package/dist/src/parsers/python.d.ts +12 -0
- package/dist/src/parsers/python.js +86 -0
- package/dist/src/parsers/python.js.map +1 -0
- package/dist/src/parsers/registry.d.ts +15 -0
- package/dist/src/parsers/registry.js +70 -0
- package/dist/src/parsers/registry.js.map +1 -0
- package/dist/src/parsers/typescript.d.ts +13 -0
- package/dist/src/parsers/typescript.js +98 -0
- package/dist/src/parsers/typescript.js.map +1 -0
- package/llms.txt +246 -236
- package/mcp-code-context-3.0.2.tgz +0 -0
- package/package.json +59 -57
- package/tsconfig.tests.json +27 -0
- package/.io/RULES.md +0 -12
- package/CONTRIBUTING.md +0 -243
- package/TROUBLESHOOTING.md +0 -38
- package/dist/src/ast/dartCompressor.d.ts +0 -27
- package/dist/src/ast/dartCompressor.js +0 -859
- package/dist/src/ast/dartCompressor.js.map +0 -1
- package/dist/src/ast/phpCompressor.d.ts +0 -24
- package/dist/src/ast/phpCompressor.js +0 -634
- package/dist/src/ast/phpCompressor.js.map +0 -1
- package/dist/src/ast/semanticCompressor.d.ts +0 -22
- package/dist/src/ast/semanticCompressor.js +0 -677
- package/dist/src/ast/semanticCompressor.js.map +0 -1
- package/dist/src/ast/writers/dartWriter.d.ts +0 -37
- package/dist/src/ast/writers/dartWriter.js +0 -544
- package/dist/src/ast/writers/dartWriter.js.map +0 -1
- package/dist/src/ast/writers/phpWriter.d.ts +0 -37
- package/dist/src/ast/writers/phpWriter.js +0 -411
- package/dist/src/ast/writers/phpWriter.js.map +0 -1
- package/dist/src/ast/writers/pyWriter.d.ts +0 -37
- package/dist/src/ast/writers/pyWriter.js +0 -355
- package/dist/src/ast/writers/pyWriter.js.map +0 -1
- package/dist/src/ast/writers/safeRename.d.ts +0 -14
- package/dist/src/ast/writers/safeRename.js +0 -258
- package/dist/src/ast/writers/safeRename.js.map +0 -1
- package/dist/src/ast/writers/symbolWriter.d.ts +0 -64
- package/dist/src/ast/writers/symbolWriter.js +0 -174
- package/dist/src/ast/writers/symbolWriter.js.map +0 -1
- package/dist/src/ast/writers/tsWriter.d.ts +0 -38
- package/dist/src/ast/writers/tsWriter.js +0 -344
- package/dist/src/ast/writers/tsWriter.js.map +0 -1
- package/dist/src/cache/astCache.d.ts +0 -39
- package/dist/src/cache/astCache.js +0 -169
- package/dist/src/cache/astCache.js.map +0 -1
- package/dist/src/handlers/readHandlers.d.ts +0 -70
- package/dist/src/handlers/readHandlers.js +0 -455
- package/dist/src/handlers/readHandlers.js.map +0 -1
- package/dist/src/handlers/utilHandlers.d.ts +0 -31
- package/dist/src/handlers/utilHandlers.js +0 -69
- package/dist/src/handlers/utilHandlers.js.map +0 -1
- package/dist/src/handlers/writeHandlers.d.ts +0 -57
- package/dist/src/handlers/writeHandlers.js +0 -499
- package/dist/src/handlers/writeHandlers.js.map +0 -1
- package/dist/src/tools/readFileLines.d.ts +0 -26
- package/dist/src/tools/readFileLines.js +0 -77
- package/dist/src/tools/readFileLines.js.map +0 -1
- package/dist/src/tools/searchCodePattern.d.ts +0 -37
- package/dist/src/tools/searchCodePattern.js +0 -144
- package/dist/src/tools/searchCodePattern.js.map +0 -1
- package/dist/src/utils/arrayUtils.d.ts +0 -12
- package/dist/src/utils/arrayUtils.js +0 -27
- package/dist/src/utils/arrayUtils.js.map +0 -1
- package/dist/src/utils/backupManager.d.ts +0 -31
- package/dist/src/utils/backupManager.js +0 -124
- package/dist/src/utils/backupManager.js.map +0 -1
- package/dist/src/utils/confirmationCache.d.ts +0 -16
- package/dist/src/utils/confirmationCache.js +0 -40
- package/dist/src/utils/confirmationCache.js.map +0 -1
- package/dist/src/utils/constants.d.ts +0 -46
- package/dist/src/utils/constants.js +0 -112
- package/dist/src/utils/constants.js.map +0 -1
- package/dist/src/utils/dependencyChecker.d.ts +0 -1
- package/dist/src/utils/dependencyChecker.js +0 -55
- package/dist/src/utils/dependencyChecker.js.map +0 -1
- package/dist/src/utils/diffEngine.d.ts +0 -21
- package/dist/src/utils/diffEngine.js +0 -163
- package/dist/src/utils/diffEngine.js.map +0 -1
- package/dist/src/utils/fuzzyMatch.d.ts +0 -8
- package/dist/src/utils/fuzzyMatch.js +0 -70
- package/dist/src/utils/fuzzyMatch.js.map +0 -1
- package/dist/src/utils/ignoreManager.d.ts +0 -29
- package/dist/src/utils/ignoreManager.js +0 -144
- package/dist/src/utils/ignoreManager.js.map +0 -1
- package/dist/src/utils/normalization.d.ts +0 -49
- package/dist/src/utils/normalization.js +0 -173
- package/dist/src/utils/normalization.js.map +0 -1
- package/dist/src/utils/projectRoot.d.ts +0 -17
- package/dist/src/utils/projectRoot.js +0 -42
- package/dist/src/utils/projectRoot.js.map +0 -1
- package/dist/src/utils/secureValidation.d.ts +0 -15
- package/dist/src/utils/secureValidation.js +0 -41
- package/dist/src/utils/secureValidation.js.map +0 -1
- package/dist/src/utils/syntaxValidator.d.ts +0 -15
- package/dist/src/utils/syntaxValidator.js +0 -218
- package/dist/src/utils/syntaxValidator.js.map +0 -1
- package/dist/src/utils/transactionManager.d.ts +0 -46
- package/dist/src/utils/transactionManager.js +0 -123
- package/dist/src/utils/transactionManager.js.map +0 -1
- package/dist/src/utils/validation.d.ts +0 -87
- package/dist/src/utils/validation.js +0 -231
- package/dist/src/utils/validation.js.map +0 -1
- package/docs/ARCHITECTURE.md +0 -300
- package/docs/architecture/ADR-001-modular-handlers.md +0 -119
package/dist/src/index.js
CHANGED
|
@@ -1,465 +1,250 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* mcp-code-context
|
|
3
|
+
* mcp-code-context v3.0.4 - Tree-sitter WASM Edition
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* - Util handlers: rollback_file, clean_backups
|
|
12
|
-
*
|
|
13
|
-
* Transport: stdio (JSON-RPC over stdin/stdout)
|
|
14
|
-
* Compatible with: Claude Desktop, Cursor, Windsurf, Copilot, Amazon Q, and any MCP client.
|
|
5
|
+
* Complete rewrite with:
|
|
6
|
+
* - Tree-sitter WASM for 100% AST accuracy (TypeScript, Python, PHP, Dart)
|
|
7
|
+
* - Zero native dependencies (no Visual Studio, node-gyp, or Python required)
|
|
8
|
+
* - 100% portable across Windows/Mac/Linux
|
|
9
|
+
* - Async-first architecture
|
|
10
|
+
* - Mandatory security boundaries
|
|
15
11
|
*/
|
|
16
12
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
17
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
14
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
import { CodeContextEngine } from "./core/engine.js";
|
|
16
|
+
import { ParserRegistry } from "./parsers/registry.js";
|
|
17
|
+
import { SecurityValidator } from "./core/validator.js";
|
|
18
|
+
import { replaceSymbol, insertCode, removeSymbol, writeFile } from "./operations/write.js";
|
|
19
|
+
const SERVER_NAME = "mcp-code-context";
|
|
20
|
+
const SERVER_VERSION = "3.0.3";
|
|
21
|
+
// Global instances
|
|
22
|
+
let engine;
|
|
23
|
+
let registry;
|
|
25
24
|
const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
|
|
26
|
-
// ─── Tool Definitions ───────────────────────────────────────────────
|
|
27
25
|
const TOOLS = [
|
|
28
26
|
{
|
|
29
|
-
name: "
|
|
30
|
-
description: "
|
|
31
|
-
"Walks the directory tree respecting .gitignore rules, reads source files, " +
|
|
32
|
-
"and extracts only structural signatures (functions, classes, interfaces, types) " +
|
|
33
|
-
"while stripping implementation bodies. Returns an XML or Markdown document " +
|
|
34
|
-
"optimized for LLM consumption with minimal token usage. " +
|
|
35
|
-
"Supports TypeScript, JavaScript, PHP (AST-based), Dart, and Python. " +
|
|
36
|
-
"Use this to understand the architecture of a large codebase at a glance.",
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: "object",
|
|
39
|
-
properties: {
|
|
40
|
-
directoryPath: {
|
|
41
|
-
type: "string",
|
|
42
|
-
description: "Absolute path to the repository root directory to analyze.",
|
|
43
|
-
},
|
|
44
|
-
format: {
|
|
45
|
-
type: "string",
|
|
46
|
-
enum: ["xml", "markdown"],
|
|
47
|
-
description: "Output format for the repo map. 'xml' (default) provides structured data, " +
|
|
48
|
-
"'markdown' provides human-readable output.",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
required: ["directoryPath"],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: "read_file_surgical",
|
|
56
|
-
description: "Reads the complete source code of a file, or surgically extracts only " +
|
|
57
|
-
"a specific named symbol (function, class, interface, method, type alias). " +
|
|
58
|
-
"When a symbol name is provided, only the full source code block for that " +
|
|
59
|
-
"symbol is returned — saving tokens compared to reading the entire file. " +
|
|
60
|
-
"Supports TypeScript, JavaScript, PHP, Dart, and Python files for symbol extraction.",
|
|
61
|
-
inputSchema: {
|
|
62
|
-
type: "object",
|
|
63
|
-
properties: {
|
|
64
|
-
filePath: {
|
|
65
|
-
type: "string",
|
|
66
|
-
description: "Absolute path to the source file to read.",
|
|
67
|
-
},
|
|
68
|
-
symbolName: {
|
|
69
|
-
type: "string",
|
|
70
|
-
description: "Optional. Name of a specific function, class, interface, method, " +
|
|
71
|
-
"or type alias to extract. If omitted, the entire file is returned.",
|
|
72
|
-
},
|
|
73
|
-
className: {
|
|
74
|
-
type: "string",
|
|
75
|
-
description: "Optional. Name of the class to scope the symbol search to. " +
|
|
76
|
-
"Use this when multiple classes in the same file have methods with the same name " +
|
|
77
|
-
"(e.g., multiple build() methods in Flutter). If omitted, returns the first match.",
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
required: ["filePath"],
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: "analyze_impact",
|
|
85
|
-
description: "Analyzes the impact of modifying a given file by discovering all other " +
|
|
86
|
-
"files in the repository that import or depend on it. Scans for ES import/export, " +
|
|
87
|
-
"CommonJS require(), Python import, PHP use/require_once/include, and Dart import statements. " +
|
|
88
|
-
"Returns a structured report listing all dependent files and the specific " +
|
|
89
|
-
"import statements that create the dependency. Essential for understanding " +
|
|
90
|
-
"blast radius before refactoring.",
|
|
91
|
-
inputSchema: {
|
|
92
|
-
type: "object",
|
|
93
|
-
properties: {
|
|
94
|
-
filePath: {
|
|
95
|
-
type: "string",
|
|
96
|
-
description: "Absolute path to the file being modified (the dependency target).",
|
|
97
|
-
},
|
|
98
|
-
rootDir: {
|
|
99
|
-
type: "string",
|
|
100
|
-
description: "Optional. Absolute path to the repository root. If omitted, " +
|
|
101
|
-
"the server walks up from filePath to find the nearest directory " +
|
|
102
|
-
"containing package.json, .git, or similar root markers.",
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
required: ["filePath"],
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: "read_file_lines",
|
|
110
|
-
description: "Reads specific line ranges from a file without loading the entire content. " +
|
|
111
|
-
"Supports reading by exact line range (startLine/endLine) or around a pattern match. " +
|
|
112
|
-
"More efficient than read_file_surgical when you only need a small fragment of code. " +
|
|
113
|
-
"Perfect for debugging, viewing specific code blocks, or extracting context around errors.",
|
|
114
|
-
inputSchema: {
|
|
115
|
-
type: "object",
|
|
116
|
-
properties: {
|
|
117
|
-
filePath: {
|
|
118
|
-
type: "string",
|
|
119
|
-
description: "Absolute path to the source file to read.",
|
|
120
|
-
},
|
|
121
|
-
startLine: {
|
|
122
|
-
type: "number",
|
|
123
|
-
description: "Starting line number (1-indexed). Required if using exact range mode.",
|
|
124
|
-
},
|
|
125
|
-
endLine: {
|
|
126
|
-
type: "number",
|
|
127
|
-
description: "Ending line number (1-indexed). Required if using exact range mode.",
|
|
128
|
-
},
|
|
129
|
-
aroundPattern: {
|
|
130
|
-
type: "string",
|
|
131
|
-
description: "Search pattern to find in the file. Returns lines around the first match. " +
|
|
132
|
-
"Use this mode when you don't know the exact line numbers.",
|
|
133
|
-
},
|
|
134
|
-
contextLines: {
|
|
135
|
-
type: "number",
|
|
136
|
-
description: "Number of lines to include before and after the pattern match. " +
|
|
137
|
-
"Defaults to 5. Only used with aroundPattern mode.",
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
required: ["filePath"],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: "search_code_pattern",
|
|
145
|
-
description: "Searches for code patterns across multiple files in a repository. " +
|
|
146
|
-
"Returns matches with file paths, line numbers, and optional context lines. " +
|
|
147
|
-
"Respects .gitignore rules and allows filtering by file extensions. " +
|
|
148
|
-
"More efficient than manual grep when you need structured results with context.",
|
|
149
|
-
inputSchema: {
|
|
150
|
-
type: "object",
|
|
151
|
-
properties: {
|
|
152
|
-
rootDir: {
|
|
153
|
-
type: "string",
|
|
154
|
-
description: "Absolute path to the repository root directory to search.",
|
|
155
|
-
},
|
|
156
|
-
pattern: {
|
|
157
|
-
type: "string",
|
|
158
|
-
description: "Regular expression pattern to search for. Use proper regex escaping " +
|
|
159
|
-
"(e.g., 'widget\\\\.height' to match 'widget.height').",
|
|
160
|
-
},
|
|
161
|
-
fileExtensions: {
|
|
162
|
-
type: "array",
|
|
163
|
-
items: { type: "string" },
|
|
164
|
-
description: "Array of file extensions to search (e.g., ['.ts', '.dart', '.py']). " +
|
|
165
|
-
"Defaults to common code extensions if omitted.",
|
|
166
|
-
},
|
|
167
|
-
excludeDirs: {
|
|
168
|
-
type: "array",
|
|
169
|
-
items: { type: "string" },
|
|
170
|
-
description: "Array of directory names to exclude (e.g., ['node_modules', 'dist']). " +
|
|
171
|
-
"Defaults to common build/dependency directories.",
|
|
172
|
-
},
|
|
173
|
-
showContext: {
|
|
174
|
-
type: "boolean",
|
|
175
|
-
description: "If true, includes surrounding lines for each match. Defaults to true.",
|
|
176
|
-
},
|
|
177
|
-
contextLines: {
|
|
178
|
-
type: "number",
|
|
179
|
-
description: "Number of context lines before/after each match. Defaults to 3.",
|
|
180
|
-
},
|
|
181
|
-
maxResults: {
|
|
182
|
-
type: "number",
|
|
183
|
-
description: "Maximum number of matches to return. Defaults to 50.",
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
required: ["rootDir", "pattern"],
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
name: "write_file_surgical",
|
|
191
|
-
description: "Surgically replaces the full source code of a named symbol (function, class, " +
|
|
192
|
-
"method, interface, type alias) in a file. The AI provides ONLY the replacement " +
|
|
193
|
-
"code for that specific symbol — the tool handles locating it via AST parsing " +
|
|
194
|
-
"and splicing the replacement into the correct position. Supports TypeScript, " +
|
|
195
|
-
"JavaScript, PHP, Dart, and Python. Returns a unified diff of the changes. " +
|
|
196
|
-
"Supports dry-run mode to preview changes without modifying the file.",
|
|
27
|
+
name: "parse_file",
|
|
28
|
+
description: "Parse a file using Tree-sitter and extract symbols",
|
|
197
29
|
inputSchema: {
|
|
198
30
|
type: "object",
|
|
199
31
|
properties: {
|
|
200
|
-
filePath: {
|
|
201
|
-
|
|
202
|
-
description: "Absolute path to the source file to modify.",
|
|
203
|
-
},
|
|
204
|
-
symbolName: {
|
|
205
|
-
type: "string",
|
|
206
|
-
description: "Name of the function, class, method, interface, or type alias to replace.",
|
|
207
|
-
},
|
|
208
|
-
newContent: {
|
|
209
|
-
type: "string",
|
|
210
|
-
description: "The complete replacement source code for the symbol, including its " +
|
|
211
|
-
"signature and body. Must be syntactically valid for the target language.",
|
|
212
|
-
},
|
|
213
|
-
className: {
|
|
214
|
-
type: "string",
|
|
215
|
-
description: "Optional. Name of the class to scope the symbol search to. " +
|
|
216
|
-
"Use when multiple classes have methods with the same name.",
|
|
217
|
-
},
|
|
218
|
-
confirmationToken: {
|
|
219
|
-
type: "string",
|
|
220
|
-
description: "Required for Phase 2. The token provided from the Phase 1 Dry-Run preview.",
|
|
221
|
-
},
|
|
222
|
-
confirm: {
|
|
223
|
-
type: "boolean",
|
|
224
|
-
description: "Required for Phase 2. Set to true to confirm and apply the changes.",
|
|
225
|
-
},
|
|
226
|
-
createBackup: {
|
|
227
|
-
type: "boolean",
|
|
228
|
-
description: "If true, creates a .bak copy of the file before modifying it. " +
|
|
229
|
-
"Defaults to false (opt-in).",
|
|
230
|
-
},
|
|
32
|
+
filePath: { type: "string", description: "Absolute path to file" },
|
|
33
|
+
projectRoot: { type: "string", description: "Project root (REQUIRED)" },
|
|
231
34
|
},
|
|
232
|
-
required: ["filePath", "
|
|
35
|
+
required: ["filePath", "projectRoot"],
|
|
233
36
|
},
|
|
234
37
|
},
|
|
235
38
|
{
|
|
236
|
-
name: "
|
|
237
|
-
description: "
|
|
238
|
-
"Can insert before or after a symbol, or inside a class/interface at the start or end. " +
|
|
239
|
-
"If no anchor symbol is provided, inserts at the end of the file. " +
|
|
240
|
-
"Supports TypeScript, JavaScript, PHP, Dart, and Python. " +
|
|
241
|
-
"Automatically matches the surrounding indentation style.",
|
|
39
|
+
name: "replace_symbol",
|
|
40
|
+
description: "Replace a symbol (function/class/method) with new code",
|
|
242
41
|
inputSchema: {
|
|
243
42
|
type: "object",
|
|
244
43
|
properties: {
|
|
245
|
-
filePath: {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
},
|
|
249
|
-
|
|
250
|
-
type: "string",
|
|
251
|
-
description: "The new code to insert. Must be syntactically valid.",
|
|
252
|
-
},
|
|
253
|
-
anchorSymbol: {
|
|
254
|
-
type: "string",
|
|
255
|
-
description: "Name of an existing symbol to position relative to. " +
|
|
256
|
-
"If omitted, code is inserted at the end of the file.",
|
|
257
|
-
},
|
|
258
|
-
position: {
|
|
259
|
-
type: "string",
|
|
260
|
-
enum: ["before", "after", "inside_start", "inside_end"],
|
|
261
|
-
description: "Where to insert relative to the anchor. 'before' and 'after' insert " +
|
|
262
|
-
"outside the symbol. 'inside_start' and 'inside_end' insert as members " +
|
|
263
|
-
"inside a class or interface. Defaults to 'after'.",
|
|
264
|
-
},
|
|
265
|
-
className: {
|
|
266
|
-
type: "string",
|
|
267
|
-
description: "Optional. Name of the class to scope the anchor symbol search to. " +
|
|
268
|
-
"Use when multiple classes have methods with the same name.",
|
|
269
|
-
},
|
|
270
|
-
confirmationToken: {
|
|
271
|
-
type: "string",
|
|
272
|
-
description: "Required for Phase 2. The token provided from the Phase 1 Dry-Run preview.",
|
|
273
|
-
},
|
|
274
|
-
confirm: {
|
|
275
|
-
type: "boolean",
|
|
276
|
-
description: "Required for Phase 2. Set to true to confirm and apply the changes.",
|
|
277
|
-
},
|
|
278
|
-
createBackup: {
|
|
279
|
-
type: "boolean",
|
|
280
|
-
description: "If true, backup the file before modifying.",
|
|
281
|
-
},
|
|
44
|
+
filePath: { type: "string", description: "Absolute path to file" },
|
|
45
|
+
projectRoot: { type: "string", description: "Project root (REQUIRED)" },
|
|
46
|
+
symbolName: { type: "string", description: "Symbol to replace" },
|
|
47
|
+
newContent: { type: "string", description: "New code" },
|
|
48
|
+
className: { type: "string", description: "Class name (optional, for scoping)" },
|
|
282
49
|
},
|
|
283
|
-
required: ["filePath", "
|
|
50
|
+
required: ["filePath", "projectRoot", "symbolName", "newContent"],
|
|
284
51
|
},
|
|
285
52
|
},
|
|
286
53
|
{
|
|
287
|
-
name: "
|
|
288
|
-
description: "
|
|
289
|
-
"source file AND updates all files that import or reference it. Uses AST-aware " +
|
|
290
|
-
"whole-word replacement to avoid false matches. Supports TypeScript, JavaScript, " +
|
|
291
|
-
"PHP, Dart, and Python. Returns a multi-file diff showing all changes. " +
|
|
292
|
-
"Essential for safe refactoring.",
|
|
54
|
+
name: "insert_code",
|
|
55
|
+
description: "Insert code at a specific location",
|
|
293
56
|
inputSchema: {
|
|
294
57
|
type: "object",
|
|
295
58
|
properties: {
|
|
296
|
-
filePath: {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
},
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
description: "The current name of the symbol.",
|
|
303
|
-
},
|
|
304
|
-
newName: {
|
|
305
|
-
type: "string",
|
|
306
|
-
description: "The new name for the symbol.",
|
|
307
|
-
},
|
|
308
|
-
rootDir: {
|
|
309
|
-
type: "string",
|
|
310
|
-
description: "Optional. Absolute path to the repository root. " +
|
|
311
|
-
"If omitted, auto-detected from the file path.",
|
|
312
|
-
},
|
|
313
|
-
confirmationToken: {
|
|
314
|
-
type: "string",
|
|
315
|
-
description: "Required for Phase 2. The token provided from the Phase 1 Dry-Run preview.",
|
|
316
|
-
},
|
|
317
|
-
confirm: {
|
|
318
|
-
type: "boolean",
|
|
319
|
-
description: "Required for Phase 2. Set to true to confirm and apply the changes.",
|
|
320
|
-
},
|
|
321
|
-
createBackup: {
|
|
322
|
-
type: "boolean",
|
|
323
|
-
description: "If true, backup each modified file before changing it.",
|
|
324
|
-
},
|
|
59
|
+
filePath: { type: "string", description: "Absolute path to file" },
|
|
60
|
+
projectRoot: { type: "string", description: "Project root (REQUIRED)" },
|
|
61
|
+
code: { type: "string", description: "Code to insert" },
|
|
62
|
+
anchorSymbol: { type: "string", description: "Symbol to position relative to" },
|
|
63
|
+
position: { type: "string", enum: ["before", "after", "inside_start", "inside_end"], description: "Where to insert" },
|
|
64
|
+
className: { type: "string", description: "Class name (optional)" },
|
|
325
65
|
},
|
|
326
|
-
required: ["filePath", "
|
|
66
|
+
required: ["filePath", "projectRoot", "code"],
|
|
327
67
|
},
|
|
328
68
|
},
|
|
329
69
|
{
|
|
330
70
|
name: "remove_symbol",
|
|
331
|
-
description: "
|
|
332
|
-
"and refuses to remove if other files import or reference the symbol. " +
|
|
333
|
-
"Use force=true to skip the dependency check and remove anyway. " +
|
|
334
|
-
"Supports TypeScript, JavaScript, PHP, Dart, and Python. " +
|
|
335
|
-
"Returns a diff showing what was removed.",
|
|
336
|
-
inputSchema: {
|
|
337
|
-
type: "object",
|
|
338
|
-
properties: {
|
|
339
|
-
filePath: {
|
|
340
|
-
type: "string",
|
|
341
|
-
description: "Absolute path to the source file.",
|
|
342
|
-
},
|
|
343
|
-
symbolName: {
|
|
344
|
-
type: "string",
|
|
345
|
-
description: "Name of the symbol to remove.",
|
|
346
|
-
},
|
|
347
|
-
className: {
|
|
348
|
-
type: "string",
|
|
349
|
-
description: "Optional. Name of the class to scope the symbol search to. " +
|
|
350
|
-
"Use when multiple classes have methods with the same name.",
|
|
351
|
-
},
|
|
352
|
-
force: {
|
|
353
|
-
type: "boolean",
|
|
354
|
-
description: "If true, skip the dependency check and remove the symbol even if " +
|
|
355
|
-
"other files reference it. Defaults to false.",
|
|
356
|
-
},
|
|
357
|
-
confirmationToken: {
|
|
358
|
-
type: "string",
|
|
359
|
-
description: "Required for Phase 2. The token provided from the Phase 1 Dry-Run preview.",
|
|
360
|
-
},
|
|
361
|
-
confirm: {
|
|
362
|
-
type: "boolean",
|
|
363
|
-
description: "Required for Phase 2. Set to true to confirm and apply the changes.",
|
|
364
|
-
},
|
|
365
|
-
createBackup: {
|
|
366
|
-
type: "boolean",
|
|
367
|
-
description: "If true, backup the file before modifying.",
|
|
368
|
-
},
|
|
369
|
-
},
|
|
370
|
-
required: ["filePath", "symbolName"],
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
name: "rollback_file",
|
|
375
|
-
description: "Reverts a file to a previous backup state after a surgical write operation. Keeps up to 5 automatic backups. Specify the number of steps to rollback (1 = most recent).",
|
|
376
|
-
inputSchema: {
|
|
377
|
-
type: "object",
|
|
378
|
-
properties: {
|
|
379
|
-
filePath: {
|
|
380
|
-
type: "string",
|
|
381
|
-
description: "Absolute path to the source file to revert.",
|
|
382
|
-
},
|
|
383
|
-
steps: {
|
|
384
|
-
type: "number",
|
|
385
|
-
description: "Number of steps back to revert to (1-5). Defaults to 1 (most recent backup).",
|
|
386
|
-
}
|
|
387
|
-
},
|
|
388
|
-
required: ["filePath"],
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
name: "clean_backups",
|
|
393
|
-
description: "Removes all backup files for a project. Deletes the entire .mcp-backups directory " +
|
|
394
|
-
"at the project root, freeing up disk space and cleaning the working directory. " +
|
|
395
|
-
"Use this to clean up after completing a series of edits or when backups are no longer needed.",
|
|
71
|
+
description: "Remove a symbol from file",
|
|
396
72
|
inputSchema: {
|
|
397
73
|
type: "object",
|
|
398
74
|
properties: {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
},
|
|
75
|
+
filePath: { type: "string", description: "Absolute path to file" },
|
|
76
|
+
projectRoot: { type: "string", description: "Project root (REQUIRED)" },
|
|
77
|
+
symbolName: { type: "string", description: "Symbol to remove" },
|
|
78
|
+
className: { type: "string", description: "Class name (optional)" },
|
|
404
79
|
},
|
|
405
|
-
required: ["projectRoot"],
|
|
80
|
+
required: ["filePath", "projectRoot", "symbolName"],
|
|
406
81
|
},
|
|
407
82
|
},
|
|
408
83
|
];
|
|
409
84
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
410
85
|
tools: TOOLS,
|
|
411
86
|
}));
|
|
412
|
-
// ─── Tool Dispatcher ────────────────────────────────────────────────
|
|
413
87
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
414
88
|
const { name, arguments: args } = request.params;
|
|
415
89
|
try {
|
|
416
90
|
switch (name) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return await handleAnalyzeImpact(args);
|
|
424
|
-
case "read_file_lines":
|
|
425
|
-
return await handleReadFileLines(args);
|
|
426
|
-
case "search_code_pattern":
|
|
427
|
-
return await handleSearchCodePattern(args);
|
|
428
|
-
// Write tools
|
|
429
|
-
case "write_file_surgical":
|
|
430
|
-
return await handleWriteFileSurgical(args);
|
|
431
|
-
case "insert_symbol":
|
|
432
|
-
return await handleInsertSymbol(args);
|
|
433
|
-
case "rename_symbol":
|
|
434
|
-
return await handleRenameSymbol(args);
|
|
91
|
+
case "parse_file":
|
|
92
|
+
return await handleParseFile(args);
|
|
93
|
+
case "replace_symbol":
|
|
94
|
+
return await handleReplaceSymbol(args);
|
|
95
|
+
case "insert_code":
|
|
96
|
+
return await handleInsertCode(args);
|
|
435
97
|
case "remove_symbol":
|
|
436
98
|
return await handleRemoveSymbol(args);
|
|
437
|
-
// Util tools
|
|
438
|
-
case "rollback_file":
|
|
439
|
-
return await handleRollbackFile(args);
|
|
440
|
-
case "clean_backups":
|
|
441
|
-
return await handleCleanBackups(args);
|
|
442
99
|
default:
|
|
443
100
|
return errorResponse(`Unknown tool: "${name}"`);
|
|
444
101
|
}
|
|
445
102
|
}
|
|
446
103
|
catch (error) {
|
|
447
104
|
const message = error instanceof Error ? error.message : String(error);
|
|
448
|
-
return errorResponse(`
|
|
105
|
+
return errorResponse(`Error in tool "${name}": ${message}`);
|
|
449
106
|
}
|
|
450
107
|
});
|
|
451
|
-
|
|
108
|
+
async function handleParseFile(args) {
|
|
109
|
+
const filePath = args.filePath;
|
|
110
|
+
const projectRoot = args.projectRoot;
|
|
111
|
+
if (!filePath)
|
|
112
|
+
return errorResponse("Missing required parameter: filePath");
|
|
113
|
+
if (!projectRoot)
|
|
114
|
+
return errorResponse("Missing required parameter: projectRoot");
|
|
115
|
+
const validator = new SecurityValidator(projectRoot);
|
|
116
|
+
const validation = await validator.validateFilePath(filePath);
|
|
117
|
+
if (!validation.valid) {
|
|
118
|
+
return errorResponse(validation.error);
|
|
119
|
+
}
|
|
120
|
+
const fs = await import("node:fs/promises");
|
|
121
|
+
const content = await fs.readFile(validation.resolvedPath, "utf-8");
|
|
122
|
+
const path = await import("node:path");
|
|
123
|
+
const ext = path.extname(validation.resolvedPath);
|
|
124
|
+
const parser = registry.getParser(ext);
|
|
125
|
+
if (!parser) {
|
|
126
|
+
return errorResponse(`No parser available for extension: ${ext}`);
|
|
127
|
+
}
|
|
128
|
+
const tree = parser.parse(content);
|
|
129
|
+
const symbols = parser.findSymbols(tree);
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: "text",
|
|
134
|
+
text: JSON.stringify({ file: filePath, symbols }, null, 2),
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function handleReplaceSymbol(args) {
|
|
140
|
+
const filePath = args.filePath;
|
|
141
|
+
const projectRoot = args.projectRoot;
|
|
142
|
+
const symbolName = args.symbolName;
|
|
143
|
+
const newContent = args.newContent;
|
|
144
|
+
const className = args.className;
|
|
145
|
+
if (!filePath)
|
|
146
|
+
return errorResponse("Missing: filePath");
|
|
147
|
+
if (!projectRoot)
|
|
148
|
+
return errorResponse("Missing: projectRoot");
|
|
149
|
+
if (!symbolName)
|
|
150
|
+
return errorResponse("Missing: symbolName");
|
|
151
|
+
if (!newContent)
|
|
152
|
+
return errorResponse("Missing: newContent");
|
|
153
|
+
const path = await import("node:path");
|
|
154
|
+
const ext = path.extname(filePath);
|
|
155
|
+
const parser = registry.getParser(ext);
|
|
156
|
+
if (!parser)
|
|
157
|
+
return errorResponse(`No parser for: ${ext}`);
|
|
158
|
+
const result = await replaceSymbol({ filePath, projectRoot, symbolName, newContent, className, parser });
|
|
159
|
+
if (!result.success) {
|
|
160
|
+
return errorResponse(result.error);
|
|
161
|
+
}
|
|
162
|
+
// Write to file
|
|
163
|
+
const validator = new SecurityValidator(projectRoot);
|
|
164
|
+
const validation = await validator.validateFilePath(filePath);
|
|
165
|
+
if (validation.valid) {
|
|
166
|
+
await writeFile(validation.resolvedPath, result.newContent);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
content: [{ type: "text", text: `✅ Replaced "${symbolName}"\n\n${result.diff}` }],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function handleInsertCode(args) {
|
|
173
|
+
const filePath = args.filePath;
|
|
174
|
+
const projectRoot = args.projectRoot;
|
|
175
|
+
const code = args.code;
|
|
176
|
+
const anchorSymbol = args.anchorSymbol;
|
|
177
|
+
const position = args.position;
|
|
178
|
+
const className = args.className;
|
|
179
|
+
if (!filePath)
|
|
180
|
+
return errorResponse("Missing: filePath");
|
|
181
|
+
if (!projectRoot)
|
|
182
|
+
return errorResponse("Missing: projectRoot");
|
|
183
|
+
if (!code)
|
|
184
|
+
return errorResponse("Missing: code");
|
|
185
|
+
const path = await import("node:path");
|
|
186
|
+
const ext = path.extname(filePath);
|
|
187
|
+
const parser = registry.getParser(ext);
|
|
188
|
+
if (!parser)
|
|
189
|
+
return errorResponse(`No parser for: ${ext}`);
|
|
190
|
+
const result = await insertCode({ filePath, projectRoot, code, anchorSymbol, position, className, parser });
|
|
191
|
+
if (!result.success) {
|
|
192
|
+
return errorResponse(result.error);
|
|
193
|
+
}
|
|
194
|
+
const validator = new SecurityValidator(projectRoot);
|
|
195
|
+
const validation = await validator.validateFilePath(filePath);
|
|
196
|
+
if (validation.valid) {
|
|
197
|
+
await writeFile(validation.resolvedPath, result.newContent);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
content: [{ type: "text", text: `✅ Code inserted\n\n${result.diff}` }],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async function handleRemoveSymbol(args) {
|
|
204
|
+
const filePath = args.filePath;
|
|
205
|
+
const projectRoot = args.projectRoot;
|
|
206
|
+
const symbolName = args.symbolName;
|
|
207
|
+
const className = args.className;
|
|
208
|
+
if (!filePath)
|
|
209
|
+
return errorResponse("Missing: filePath");
|
|
210
|
+
if (!projectRoot)
|
|
211
|
+
return errorResponse("Missing: projectRoot");
|
|
212
|
+
if (!symbolName)
|
|
213
|
+
return errorResponse("Missing: symbolName");
|
|
214
|
+
const path = await import("node:path");
|
|
215
|
+
const ext = path.extname(filePath);
|
|
216
|
+
const parser = registry.getParser(ext);
|
|
217
|
+
if (!parser)
|
|
218
|
+
return errorResponse(`No parser for: ${ext}`);
|
|
219
|
+
const result = await removeSymbol({ filePath, projectRoot, symbolName, className, parser });
|
|
220
|
+
if (!result.success) {
|
|
221
|
+
return errorResponse(result.error);
|
|
222
|
+
}
|
|
223
|
+
const validator = new SecurityValidator(projectRoot);
|
|
224
|
+
const validation = await validator.validateFilePath(filePath);
|
|
225
|
+
if (validation.valid) {
|
|
226
|
+
await writeFile(validation.resolvedPath, result.newContent);
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
content: [{ type: "text", text: `✅ Removed "${symbolName}"\n\n${result.diff}` }],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
452
232
|
function errorResponse(message) {
|
|
453
233
|
return {
|
|
454
234
|
content: [{ type: "text", text: `❌ ${message}` }],
|
|
455
235
|
isError: true,
|
|
456
236
|
};
|
|
457
237
|
}
|
|
458
|
-
// ─── Bootstrap ──────────────────────────────────────────────────────
|
|
459
238
|
async function main() {
|
|
239
|
+
console.error("🚀 Initializing mcp-code-context v3.0.4 (WASM)...");
|
|
240
|
+
engine = new CodeContextEngine();
|
|
241
|
+
await engine.init();
|
|
242
|
+
registry = new ParserRegistry(engine);
|
|
243
|
+
await registry.init();
|
|
244
|
+
console.error("✅ Engine initialized with Tree-sitter WASM (portable)");
|
|
460
245
|
const transport = new StdioServerTransport();
|
|
461
246
|
await server.connect(transport);
|
|
462
|
-
console.error(`🚀 ${SERVER_NAME} v${SERVER_VERSION} running on stdio
|
|
247
|
+
console.error(`🚀 ${SERVER_NAME} v${SERVER_VERSION} running on stdio`);
|
|
463
248
|
}
|
|
464
249
|
import { pathToFileURL } from "node:url";
|
|
465
250
|
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|