mcp-code-context 2.5.1 → 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.
Files changed (129) hide show
  1. package/CHANGELOG.md +92 -236
  2. package/README.md +304 -298
  3. package/dist/src/core/engine.d.ts +21 -0
  4. package/dist/src/core/engine.js +68 -0
  5. package/dist/src/core/engine.js.map +1 -0
  6. package/dist/src/core/validator.d.ts +15 -0
  7. package/dist/src/core/validator.js +44 -0
  8. package/dist/src/core/validator.js.map +1 -0
  9. package/dist/src/index.d.ts +7 -11
  10. package/dist/src/index.js +182 -397
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/operations/write.d.ts +51 -0
  13. package/dist/src/operations/write.js +169 -0
  14. package/dist/src/operations/write.js.map +1 -0
  15. package/dist/src/parsers/base.d.ts +28 -0
  16. package/dist/src/parsers/base.js +27 -0
  17. package/dist/src/parsers/base.js.map +1 -0
  18. package/dist/src/parsers/dartTreeSitter.d.ts +13 -0
  19. package/dist/src/parsers/dartTreeSitter.js +91 -0
  20. package/dist/src/parsers/dartTreeSitter.js.map +1 -0
  21. package/dist/src/parsers/php.d.ts +12 -0
  22. package/dist/src/parsers/php.js +86 -0
  23. package/dist/src/parsers/php.js.map +1 -0
  24. package/dist/src/parsers/python.d.ts +12 -0
  25. package/dist/src/parsers/python.js +86 -0
  26. package/dist/src/parsers/python.js.map +1 -0
  27. package/dist/src/parsers/registry.d.ts +15 -0
  28. package/dist/src/parsers/registry.js +70 -0
  29. package/dist/src/parsers/registry.js.map +1 -0
  30. package/dist/src/parsers/typescript.d.ts +13 -0
  31. package/dist/src/parsers/typescript.js +98 -0
  32. package/dist/src/parsers/typescript.js.map +1 -0
  33. package/llms.txt +246 -234
  34. package/mcp-code-context-3.0.2.tgz +0 -0
  35. package/package.json +59 -57
  36. package/tsconfig.tests.json +27 -0
  37. package/.io/RULES.md +0 -12
  38. package/CONTRIBUTING.md +0 -243
  39. package/PLAN_v2.5.0.md +0 -407
  40. package/TROUBLESHOOTING.md +0 -38
  41. package/dist/src/ast/dartCompressor.d.ts +0 -27
  42. package/dist/src/ast/dartCompressor.js +0 -859
  43. package/dist/src/ast/dartCompressor.js.map +0 -1
  44. package/dist/src/ast/phpCompressor.d.ts +0 -24
  45. package/dist/src/ast/phpCompressor.js +0 -634
  46. package/dist/src/ast/phpCompressor.js.map +0 -1
  47. package/dist/src/ast/semanticCompressor.d.ts +0 -22
  48. package/dist/src/ast/semanticCompressor.js +0 -677
  49. package/dist/src/ast/semanticCompressor.js.map +0 -1
  50. package/dist/src/ast/writers/dartWriter.d.ts +0 -37
  51. package/dist/src/ast/writers/dartWriter.js +0 -544
  52. package/dist/src/ast/writers/dartWriter.js.map +0 -1
  53. package/dist/src/ast/writers/phpWriter.d.ts +0 -37
  54. package/dist/src/ast/writers/phpWriter.js +0 -411
  55. package/dist/src/ast/writers/phpWriter.js.map +0 -1
  56. package/dist/src/ast/writers/pyWriter.d.ts +0 -37
  57. package/dist/src/ast/writers/pyWriter.js +0 -355
  58. package/dist/src/ast/writers/pyWriter.js.map +0 -1
  59. package/dist/src/ast/writers/safeRename.d.ts +0 -14
  60. package/dist/src/ast/writers/safeRename.js +0 -258
  61. package/dist/src/ast/writers/safeRename.js.map +0 -1
  62. package/dist/src/ast/writers/symbolWriter.d.ts +0 -64
  63. package/dist/src/ast/writers/symbolWriter.js +0 -174
  64. package/dist/src/ast/writers/symbolWriter.js.map +0 -1
  65. package/dist/src/ast/writers/tsWriter.d.ts +0 -38
  66. package/dist/src/ast/writers/tsWriter.js +0 -344
  67. package/dist/src/ast/writers/tsWriter.js.map +0 -1
  68. package/dist/src/cache/astCache.d.ts +0 -39
  69. package/dist/src/cache/astCache.js +0 -169
  70. package/dist/src/cache/astCache.js.map +0 -1
  71. package/dist/src/handlers/readHandlers.d.ts +0 -70
  72. package/dist/src/handlers/readHandlers.js +0 -455
  73. package/dist/src/handlers/readHandlers.js.map +0 -1
  74. package/dist/src/handlers/utilHandlers.d.ts +0 -31
  75. package/dist/src/handlers/utilHandlers.js +0 -69
  76. package/dist/src/handlers/utilHandlers.js.map +0 -1
  77. package/dist/src/handlers/writeHandlers.d.ts +0 -57
  78. package/dist/src/handlers/writeHandlers.js +0 -499
  79. package/dist/src/handlers/writeHandlers.js.map +0 -1
  80. package/dist/src/tools/readFileLines.d.ts +0 -26
  81. package/dist/src/tools/readFileLines.js +0 -77
  82. package/dist/src/tools/readFileLines.js.map +0 -1
  83. package/dist/src/tools/searchCodePattern.d.ts +0 -37
  84. package/dist/src/tools/searchCodePattern.js +0 -144
  85. package/dist/src/tools/searchCodePattern.js.map +0 -1
  86. package/dist/src/utils/arrayUtils.d.ts +0 -12
  87. package/dist/src/utils/arrayUtils.js +0 -27
  88. package/dist/src/utils/arrayUtils.js.map +0 -1
  89. package/dist/src/utils/backupManager.d.ts +0 -31
  90. package/dist/src/utils/backupManager.js +0 -124
  91. package/dist/src/utils/backupManager.js.map +0 -1
  92. package/dist/src/utils/confirmationCache.d.ts +0 -16
  93. package/dist/src/utils/confirmationCache.js +0 -40
  94. package/dist/src/utils/confirmationCache.js.map +0 -1
  95. package/dist/src/utils/constants.d.ts +0 -46
  96. package/dist/src/utils/constants.js +0 -112
  97. package/dist/src/utils/constants.js.map +0 -1
  98. package/dist/src/utils/dependencyChecker.d.ts +0 -1
  99. package/dist/src/utils/dependencyChecker.js +0 -55
  100. package/dist/src/utils/dependencyChecker.js.map +0 -1
  101. package/dist/src/utils/diffEngine.d.ts +0 -21
  102. package/dist/src/utils/diffEngine.js +0 -163
  103. package/dist/src/utils/diffEngine.js.map +0 -1
  104. package/dist/src/utils/fuzzyMatch.d.ts +0 -8
  105. package/dist/src/utils/fuzzyMatch.js +0 -69
  106. package/dist/src/utils/fuzzyMatch.js.map +0 -1
  107. package/dist/src/utils/ignoreManager.d.ts +0 -30
  108. package/dist/src/utils/ignoreManager.js +0 -186
  109. package/dist/src/utils/ignoreManager.js.map +0 -1
  110. package/dist/src/utils/normalization.d.ts +0 -49
  111. package/dist/src/utils/normalization.js +0 -173
  112. package/dist/src/utils/normalization.js.map +0 -1
  113. package/dist/src/utils/projectRoot.d.ts +0 -17
  114. package/dist/src/utils/projectRoot.js +0 -42
  115. package/dist/src/utils/projectRoot.js.map +0 -1
  116. package/dist/src/utils/secureValidation.d.ts +0 -15
  117. package/dist/src/utils/secureValidation.js +0 -41
  118. package/dist/src/utils/secureValidation.js.map +0 -1
  119. package/dist/src/utils/syntaxValidator.d.ts +0 -15
  120. package/dist/src/utils/syntaxValidator.js +0 -218
  121. package/dist/src/utils/syntaxValidator.js.map +0 -1
  122. package/dist/src/utils/transactionManager.d.ts +0 -46
  123. package/dist/src/utils/transactionManager.js +0 -123
  124. package/dist/src/utils/transactionManager.js.map +0 -1
  125. package/dist/src/utils/validation.d.ts +0 -87
  126. package/dist/src/utils/validation.js +0 -231
  127. package/dist/src/utils/validation.js.map +0 -1
  128. package/docs/ARCHITECTURE.md +0 -300
  129. 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 — Semantic Code Context Server (v2.3.1 - Refactored)
3
+ * mcp-code-context v3.0.4 - Tree-sitter WASM Edition
4
4
  *
5
- * A Model Context Protocol (MCP) server that compresses any code repository
6
- * into LLM-ready semantic context and provides surgical code editing.
7
- *
8
- * Architecture: Modular handlers with centralized utilities
9
- * - Read handlers: get_semantic_repo_map, read_file_surgical, analyze_impact, etc.
10
- * - Write handlers: write_file_surgical, insert_symbol, rename_symbol, remove_symbol
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
- // Import handlers
20
- import { handleGetSemanticRepoMap, handleReadFileSurgical, handleAnalyzeImpact, handleReadFileLines, handleSearchCodePattern, } from "./handlers/readHandlers.js";
21
- import { handleWriteFileSurgical, handleInsertSymbol, handleRenameSymbol, handleRemoveSymbol, } from "./handlers/writeHandlers.js";
22
- import { handleRollbackFile, handleCleanBackups, } from "./handlers/utilHandlers.js";
23
- import { SERVER_NAME, SERVER_VERSION } from "./utils/constants.js";
24
- // ─── MCP Server Instance ────────────────────────────────────────────
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: "get_semantic_repo_map",
30
- description: "Generates a semantically compressed map of a code repository. " +
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
- type: "string",
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", "symbolName", "newContent"],
35
+ required: ["filePath", "projectRoot"],
233
36
  },
234
37
  },
235
38
  {
236
- name: "insert_symbol",
237
- description: "Inserts new code at a precise location relative to an existing symbol in a file. " +
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
- type: "string",
247
- description: "Absolute path to the source file to modify.",
248
- },
249
- code: {
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", "code"],
50
+ required: ["filePath", "projectRoot", "symbolName", "newContent"],
284
51
  },
285
52
  },
286
53
  {
287
- name: "rename_symbol",
288
- description: "Renames a symbol across an entire repository. Renames the definition in the " +
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
- type: "string",
298
- description: "Absolute path to the file where the symbol is defined.",
299
- },
300
- oldName: {
301
- type: "string",
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", "oldName", "newName"],
66
+ required: ["filePath", "projectRoot", "code"],
327
67
  },
328
68
  },
329
69
  {
330
70
  name: "remove_symbol",
331
- description: "Removes a named symbol from a file. By default, checks for dependents first " +
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
- projectRoot: {
400
- type: "string",
401
- description: "Absolute path to the project root directory. The .mcp-backups folder " +
402
- "at this location will be removed.",
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
- // Read tools
418
- case "get_semantic_repo_map":
419
- return await handleGetSemanticRepoMap(args);
420
- case "read_file_surgical":
421
- return await handleReadFileSurgical(args);
422
- case "analyze_impact":
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(`Unhandled error in tool "${name}": ${message}`);
105
+ return errorResponse(`Error in tool "${name}": ${message}`);
449
106
  }
450
107
  });
451
- // ─── Utility ────────────────────────────────────────────────────────
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 transport`);
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) {