@n0zer0d4y/vulcan-file-ops 1.0.1 → 1.1.1
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 +35 -0
- package/README.md +268 -31
- package/dist/tools/write-tools.js +218 -22
- package/dist/types/index.js +72 -5
- package/dist/utils/lib.js +7 -1
- package/package.json +27 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.1] - 2025-11-11
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Corrected Docker entrypoint to use dist/cli.js instead of dist/index.js for proper MCP server initialization
|
|
13
|
+
- Updated Node.js base image from node:22.12-alpine to node:22-alpine for better version compatibility
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Added keywords to package.json for improved NPM discoverability
|
|
18
|
+
|
|
19
|
+
## [1.1.0] - 2025-11-08
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Multi-file editing capability for edit_file tool
|
|
24
|
+
- Support for editing up to 50 files in a single operation
|
|
25
|
+
- Mode discriminator (single/multiple) for backward compatibility
|
|
26
|
+
- Atomic operations with automatic rollback on failure
|
|
27
|
+
- Per-file configuration options (matching strategy, dryRun, failOnAmbiguous)
|
|
28
|
+
- Concurrent file processing for improved performance
|
|
29
|
+
- Detailed multi-file diff output with summary statistics
|
|
30
|
+
- Comprehensive test suite for multi-file editing functionality
|
|
31
|
+
- Implementation plan documentation in local_docs folder
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Enhanced edit_file tool schema to support both single and multi-file modes
|
|
36
|
+
- Updated README documentation with complete edit_file feature specification
|
|
37
|
+
- Improved EditFileArgsSchema with explicit mode parameter for better MCP client compatibility
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Test timeout issues in shell-tool.test.ts and shell-command-path-validation.test.ts
|
|
42
|
+
|
|
8
43
|
## [1.0.1] - 2025-11-03
|
|
9
44
|
|
|
10
45
|
### Security
|
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# Vulcan File Ops MCP Server
|
|
2
2
|
|
|
3
3
|

|
|
4
|
+

|
|
4
5
|
[](https://modelcontextprotocol.io)
|
|
5
6
|
[](https://modelcontextprotocol.io)
|
|
6
7
|
[](https://github.com/RichardLitt/standard-readme)
|
|
7
8
|
[](https://opensource.org/licenses/MIT)
|
|
8
9
|
|
|
9
|
-
A configurable Model Context Protocol server for secure filesystem operations that absolutely **rocks**. Enables AI assistants to dynamically access and manage file system resources with runtime directory registration and selective tool activation.
|
|
10
|
-
|
|
11
10
|
**Transform your desktop AI assistants into powerful development partners.** Vulcan File Ops bridges the gap between conversational AI (Claude Desktop, ChatGPT Desktop, etc.) and your local filesystem, unlocking the same file manipulation capabilities found in AI-powered IDEs like Cursor and Cline. Write code, refactor projects, manage documentation, and perform complex file operations—matching the power of dedicated AI coding assistants. With enterprise-grade security controls, dynamic directory registration, and intelligent tool filtering, you maintain complete control while your AI assistant handles the heavy lifting.
|
|
12
11
|
|
|
13
12
|
## Table of Contents
|
|
@@ -54,7 +53,7 @@ This enhanced implementation provides:
|
|
|
54
53
|
|
|
55
54
|
- **Dynamic Directory Access**: Runtime directory registration through conversational commands
|
|
56
55
|
- **Document Support**: Read/write PDF, DOCX, PPTX, XLSX, ODT with HTML-to-document conversion
|
|
57
|
-
- **Batch Operations**: Read, write, copy, move, or rename multiple files concurrently
|
|
56
|
+
- **Batch Operations**: Read, write, edit, copy, move, or rename multiple files concurrently
|
|
58
57
|
- **Advanced File Editing**: Pattern-based modifications with flexible matching and diff preview
|
|
59
58
|
- **Flexible Reading Modes**: Full file, head/tail, or arbitrary line ranges
|
|
60
59
|
- **Image Vision Support**: Attach images for AI analysis and description
|
|
@@ -76,28 +75,70 @@ This server supports multiple flexible approaches to directory access:
|
|
|
76
75
|
|
|
77
76
|
## Install
|
|
78
77
|
|
|
79
|
-
This server requires Node.js and can be installed locally for
|
|
78
|
+
This server requires Node.js and can be installed globally, locally, or run directly with npx. **Most users should use npx** for instant execution without installation.
|
|
79
|
+
|
|
80
|
+
### Quick Start (Recommended for Most Users)
|
|
81
|
+
|
|
82
|
+
Run directly without installation:
|
|
80
83
|
|
|
81
84
|
```bash
|
|
82
|
-
|
|
85
|
+
npx @n0zer0d4y/vulcan-file-ops --help
|
|
83
86
|
```
|
|
84
87
|
|
|
85
|
-
|
|
88
|
+
**For developers** who want to contribute or modify the code, see [Local Repository Execution](#option-4-local-repository-execution-for-developers) below.
|
|
89
|
+
|
|
90
|
+
### Global Installation
|
|
91
|
+
|
|
92
|
+
Install globally for system-wide access:
|
|
86
93
|
|
|
87
94
|
```bash
|
|
88
|
-
npm install vulcan-file-ops
|
|
95
|
+
npm install -g @n0zer0d4y/vulcan-file-ops
|
|
89
96
|
```
|
|
90
97
|
|
|
98
|
+
### Local Installation
|
|
99
|
+
|
|
100
|
+
Install in a specific project:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm install @n0zer0d4y/vulcan-file-ops
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Prerequisites
|
|
107
|
+
|
|
108
|
+
**Node.js** (version 14 or higher) must be installed on your system. This provides npm and npx, which are required to run this package.
|
|
109
|
+
|
|
110
|
+
- **Download Node.js**: https://nodejs.org/
|
|
111
|
+
- **Check installation**: Run `node --version` and `npm --version`
|
|
112
|
+
|
|
91
113
|
### Dependencies
|
|
92
114
|
|
|
93
|
-
|
|
115
|
+
The server has no external service dependencies and operates entirely locally. All required packages are automatically downloaded when using npx.
|
|
94
116
|
|
|
95
117
|
## Usage
|
|
96
118
|
|
|
119
|
+
This server can be used directly with npx (recommended) or installed globally/locally. The npx approach requires no installation and always uses the latest version.
|
|
120
|
+
|
|
97
121
|
### Basic Configuration
|
|
98
122
|
|
|
99
123
|
Add to your MCP client configuration (e.g., `claude_desktop_config.json`):
|
|
100
124
|
|
|
125
|
+
#### Option 1: Using npx (Recommended - No Installation Required)
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"vulcan-file-ops": {
|
|
131
|
+
"command": "npx",
|
|
132
|
+
"args": ["@n0zer0d4y/vulcan-file-ops"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Option 2: Using Global Installation
|
|
139
|
+
|
|
140
|
+
After running `npm install -g @n0zer0d4y/vulcan-file-ops`:
|
|
141
|
+
|
|
101
142
|
```json
|
|
102
143
|
{
|
|
103
144
|
"mcpServers": {
|
|
@@ -108,20 +149,61 @@ Add to your MCP client configuration (e.g., `claude_desktop_config.json`):
|
|
|
108
149
|
}
|
|
109
150
|
```
|
|
110
151
|
|
|
152
|
+
#### Option 3: Using Local Installation
|
|
153
|
+
|
|
154
|
+
After running `npm install @n0zer0d4y/vulcan-file-ops` in your project:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"vulcan-file-ops": {
|
|
160
|
+
"command": "./node_modules/.bin/vulcan-file-ops"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Option 4: Local Repository Execution (For Developers)
|
|
167
|
+
|
|
168
|
+
If you've cloned this repository and want to run from source:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git clone https://github.com/n0zer0d4y/vulcan-file-ops.git
|
|
172
|
+
cd vulcan-file-ops
|
|
173
|
+
npm install
|
|
174
|
+
npm run build
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Then configure your MCP client:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"mcpServers": {
|
|
182
|
+
"vulcan-file-ops": {
|
|
183
|
+
"command": "vulcan-file-ops",
|
|
184
|
+
"args": ["--approved-folders", "/path/to/your/allowed/directories"]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Note:** The `vulcan-file-ops` command will be available in your PATH after building, or you can use the full path: `./dist/cli.js`
|
|
191
|
+
|
|
111
192
|
### Advanced Configuration
|
|
112
193
|
|
|
113
194
|
#### Approved Folders
|
|
114
195
|
|
|
115
196
|
Pre-configure specific directories for immediate access on server start:
|
|
116
197
|
|
|
117
|
-
**macOS/Linux:**
|
|
198
|
+
**macOS/Linux (npx):**
|
|
118
199
|
|
|
119
200
|
```json
|
|
120
201
|
{
|
|
121
202
|
"mcpServers": {
|
|
122
203
|
"vulcan-file-ops": {
|
|
123
|
-
"command": "
|
|
204
|
+
"command": "npx",
|
|
124
205
|
"args": [
|
|
206
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
125
207
|
"--approved-folders",
|
|
126
208
|
"/Users/username/projects,/Users/username/documents"
|
|
127
209
|
]
|
|
@@ -130,14 +212,15 @@ Pre-configure specific directories for immediate access on server start:
|
|
|
130
212
|
}
|
|
131
213
|
```
|
|
132
214
|
|
|
133
|
-
**Windows:**
|
|
215
|
+
**Windows (npx):**
|
|
134
216
|
|
|
135
217
|
```json
|
|
136
218
|
{
|
|
137
219
|
"mcpServers": {
|
|
138
220
|
"vulcan-file-ops": {
|
|
139
|
-
"command": "
|
|
221
|
+
"command": "npx",
|
|
140
222
|
"args": [
|
|
223
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
141
224
|
"--approved-folders",
|
|
142
225
|
"C:/Users/username/projects,C:/Users/username/documents"
|
|
143
226
|
]
|
|
@@ -146,6 +229,24 @@ Pre-configure specific directories for immediate access on server start:
|
|
|
146
229
|
}
|
|
147
230
|
```
|
|
148
231
|
|
|
232
|
+
**Alternative: Local Repository Execution**
|
|
233
|
+
|
|
234
|
+
For users running from a cloned repository (after `npm run build`):
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"mcpServers": {
|
|
239
|
+
"vulcan-file-ops": {
|
|
240
|
+
"command": "vulcan-file-ops",
|
|
241
|
+
"args": [
|
|
242
|
+
"--approved-folders",
|
|
243
|
+
"/Users/username/projects,/Users/username/documents"
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
149
250
|
**Path Format Note:**
|
|
150
251
|
|
|
151
252
|
- **Windows**: Include drive letter (e.g., `C:/`, `D:/`). Use forward slashes in JSON to avoid escaping backslashes.
|
|
@@ -194,8 +295,12 @@ Exclude specific folders from directory listings:
|
|
|
194
295
|
{
|
|
195
296
|
"mcpServers": {
|
|
196
297
|
"vulcan-file-ops": {
|
|
197
|
-
"command": "
|
|
198
|
-
"args": [
|
|
298
|
+
"command": "npx",
|
|
299
|
+
"args": [
|
|
300
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
301
|
+
"--ignored-folders",
|
|
302
|
+
"node_modules,dist,.git,.next"
|
|
303
|
+
]
|
|
199
304
|
}
|
|
200
305
|
}
|
|
201
306
|
}
|
|
@@ -209,8 +314,12 @@ Enable only specific tool categories:
|
|
|
209
314
|
{
|
|
210
315
|
"mcpServers": {
|
|
211
316
|
"vulcan-file-ops": {
|
|
212
|
-
"command": "
|
|
213
|
-
"args": [
|
|
317
|
+
"command": "npx",
|
|
318
|
+
"args": [
|
|
319
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
320
|
+
"--enabled-tool-categories",
|
|
321
|
+
"read,filesystem"
|
|
322
|
+
]
|
|
214
323
|
}
|
|
215
324
|
}
|
|
216
325
|
}
|
|
@@ -222,8 +331,12 @@ Or enable individual tools:
|
|
|
222
331
|
{
|
|
223
332
|
"mcpServers": {
|
|
224
333
|
"vulcan-file-ops": {
|
|
225
|
-
"command": "
|
|
226
|
-
"args": [
|
|
334
|
+
"command": "npx",
|
|
335
|
+
"args": [
|
|
336
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
337
|
+
"--enabled-tools",
|
|
338
|
+
"read_file,list_directory,grep_files"
|
|
339
|
+
]
|
|
227
340
|
}
|
|
228
341
|
}
|
|
229
342
|
}
|
|
@@ -233,14 +346,15 @@ Or enable individual tools:
|
|
|
233
346
|
|
|
234
347
|
All configuration options can be combined:
|
|
235
348
|
|
|
236
|
-
**Windows Example:**
|
|
349
|
+
**Windows Example (npx):**
|
|
237
350
|
|
|
238
351
|
```json
|
|
239
352
|
{
|
|
240
353
|
"mcpServers": {
|
|
241
354
|
"vulcan-file-ops": {
|
|
242
|
-
"command": "
|
|
355
|
+
"command": "npx",
|
|
243
356
|
"args": [
|
|
357
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
244
358
|
"--approved-folders",
|
|
245
359
|
"C:/Users/username/projects,C:/Users/username/documents",
|
|
246
360
|
"--ignored-folders",
|
|
@@ -250,14 +364,41 @@ All configuration options can be combined:
|
|
|
250
364
|
"--enabled-tool-categories",
|
|
251
365
|
"read,filesystem,shell",
|
|
252
366
|
"--enabled-tools",
|
|
253
|
-
"list_directory,
|
|
367
|
+
"read_file,attach_image,read_multiple_files,write_file,write_multiple_files,edit_file,make_directory,list_directory,move_file,file_operations,delete_files,get_file_info,register_directory,list_allowed_directories,glob_files,grep_files,execute_shell"
|
|
254
368
|
]
|
|
255
369
|
}
|
|
256
370
|
}
|
|
257
371
|
}
|
|
258
372
|
```
|
|
259
373
|
|
|
260
|
-
**macOS/Linux Example:**
|
|
374
|
+
**macOS/Linux Example (npx):**
|
|
375
|
+
|
|
376
|
+
```json
|
|
377
|
+
{
|
|
378
|
+
"mcpServers": {
|
|
379
|
+
"vulcan-file-ops": {
|
|
380
|
+
"command": "npx",
|
|
381
|
+
"args": [
|
|
382
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
383
|
+
"--approved-folders",
|
|
384
|
+
"/Users/username/projects,/Users/username/documents",
|
|
385
|
+
"--ignored-folders",
|
|
386
|
+
"node_modules,dist,.git",
|
|
387
|
+
"--approved-commands",
|
|
388
|
+
"npm,node,git,ls,pwd,cat,echo",
|
|
389
|
+
"--enabled-tool-categories",
|
|
390
|
+
"read,filesystem,shell",
|
|
391
|
+
"--enabled-tools",
|
|
392
|
+
"read_file,attach_image,read_multiple_files,write_file,write_multiple_files,edit_file,make_directory,list_directory,move_file,file_operations,delete_files,get_file_info,register_directory,list_allowed_directories,glob_files,grep_files,execute_shell"
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Alternative: Local Repository Execution**
|
|
400
|
+
|
|
401
|
+
For users running from a cloned repository (after `npm run build`):
|
|
261
402
|
|
|
262
403
|
```json
|
|
263
404
|
{
|
|
@@ -274,7 +415,7 @@ All configuration options can be combined:
|
|
|
274
415
|
"--enabled-tool-categories",
|
|
275
416
|
"read,filesystem,shell",
|
|
276
417
|
"--enabled-tools",
|
|
277
|
-
"list_directory,
|
|
418
|
+
"read_file,attach_image,read_multiple_files,write_file,write_multiple_files,edit_file,make_directory,list_directory,move_file,file_operations,delete_files,get_file_info,register_directory,list_allowed_directories,glob_files,grep_files,execute_shell"
|
|
278
419
|
]
|
|
279
420
|
}
|
|
280
421
|
}
|
|
@@ -360,20 +501,46 @@ Create or replace multiple files concurrently
|
|
|
360
501
|
|
|
361
502
|
##### edit_file
|
|
362
503
|
|
|
363
|
-
|
|
504
|
+
Apply precise modifications to text and code files with intelligent matching. Supports both single-file and multi-file operations.
|
|
364
505
|
|
|
365
|
-
**Input:**
|
|
506
|
+
**Single File Input (mode: 'single'):**
|
|
366
507
|
|
|
508
|
+
- `mode` (string, optional): Set to `"single"` (default if omitted for backward compatibility)
|
|
367
509
|
- `path` (string): File path
|
|
368
|
-
- `edits` (array): List of edit operations
|
|
369
|
-
- `
|
|
510
|
+
- `edits` (array): List of edit operations, each containing:
|
|
511
|
+
- `oldText` (string): Text to search for (include 3-5 lines of context)
|
|
512
|
+
- `newText` (string): Text to replace with
|
|
513
|
+
- `instruction` (string, optional): Description of what this edit does
|
|
514
|
+
- `expectedOccurrences` (number, optional): Expected match count (default: 1)
|
|
370
515
|
- `matchingStrategy` (string, optional): Matching strategy
|
|
371
|
-
- `exact` - Character-for-character match
|
|
372
|
-
- `flexible` - Whitespace-insensitive matching
|
|
373
|
-
- `fuzzy` - Token-based regex matching
|
|
516
|
+
- `exact` - Character-for-character match (fastest, safest)
|
|
517
|
+
- `flexible` - Whitespace-insensitive matching, preserves indentation
|
|
518
|
+
- `fuzzy` - Token-based regex matching (most permissive)
|
|
374
519
|
- `auto` - Try exact → flexible → fuzzy (default)
|
|
520
|
+
- `dryRun` (boolean, optional): Preview changes without writing (default: false)
|
|
521
|
+
- `failOnAmbiguous` (boolean, optional): Fail when matches are ambiguous (default: true)
|
|
522
|
+
|
|
523
|
+
**Multi-File Input (mode: 'multiple'):**
|
|
524
|
+
|
|
525
|
+
- `mode` (string): Set to `"multiple"`
|
|
526
|
+
- `files` (array): Array of file edit requests (max 50), each containing:
|
|
527
|
+
- `path` (string): File path
|
|
528
|
+
- `edits` (array): List of edit operations for this file (same structure as above)
|
|
529
|
+
- `matchingStrategy` (string, optional): Per-file matching strategy
|
|
530
|
+
- `dryRun` (boolean, optional): Per-file dry-run mode
|
|
531
|
+
- `failOnAmbiguous` (boolean, optional): Per-file ambiguity handling
|
|
532
|
+
- `failFast` (boolean, optional): Stop on first failure with rollback (true, default) or continue (false)
|
|
375
533
|
|
|
376
|
-
**
|
|
534
|
+
**Features:**
|
|
535
|
+
|
|
536
|
+
- Concurrent processing for multi-file operations
|
|
537
|
+
- Atomic operations with automatic rollback on failure (when failFast: true)
|
|
538
|
+
- Cross-platform line ending preservation
|
|
539
|
+
- Detailed diff output with statistics
|
|
540
|
+
|
|
541
|
+
**Output:** Detailed diff with statistics. For multi-file operations, includes per-file results and summary statistics with rollback information for atomic operations.
|
|
542
|
+
|
|
543
|
+
**Important:** Use actual newline characters in oldText/newText, NOT escape sequences like `\n`.
|
|
377
544
|
|
|
378
545
|
#### Filesystem Operations
|
|
379
546
|
|
|
@@ -525,6 +692,76 @@ Execute shell commands with security controls
|
|
|
525
692
|
|
|
526
693
|
**Security:** All file/directory paths in command arguments are automatically extracted and validated against allowed directories. Commands referencing paths outside approved directories are blocked, preventing directory restriction bypasses.
|
|
527
694
|
|
|
695
|
+
### Multi-File Edit Examples
|
|
696
|
+
|
|
697
|
+
**Batch refactor across multiple files:**
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
{
|
|
701
|
+
files: [
|
|
702
|
+
{
|
|
703
|
+
path: "src/utils.ts",
|
|
704
|
+
edits: [{
|
|
705
|
+
instruction: "Update deprecated function call",
|
|
706
|
+
oldText: "oldApi.getData()",
|
|
707
|
+
newText: "newApi.fetchData()"
|
|
708
|
+
}]
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
path: "src/components/Button.tsx",
|
|
712
|
+
edits: [{
|
|
713
|
+
instruction: "Update component prop",
|
|
714
|
+
oldText: "onClick={oldHandler}",
|
|
715
|
+
newText: "onClick={newHandler}"
|
|
716
|
+
}]
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
path: "src/hooks/useData.ts",
|
|
720
|
+
edits: [{
|
|
721
|
+
instruction: "Update hook implementation",
|
|
722
|
+
oldText: "const data = oldApi.getData()",
|
|
723
|
+
newText: "const data = newApi.fetchData()"
|
|
724
|
+
}]
|
|
725
|
+
}
|
|
726
|
+
],
|
|
727
|
+
failFast: true // Atomic operation - rollback all if any fails
|
|
728
|
+
}
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Per-file configuration:**
|
|
732
|
+
|
|
733
|
+
```typescript
|
|
734
|
+
{
|
|
735
|
+
files: [
|
|
736
|
+
{
|
|
737
|
+
path: "config.json",
|
|
738
|
+
edits: [{
|
|
739
|
+
oldText: '"version": "1.0.0"',
|
|
740
|
+
newText: '"version": "1.1.0"'
|
|
741
|
+
}],
|
|
742
|
+
matchingStrategy: "exact" // JSON needs exact matches
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
path: "src/app.py",
|
|
746
|
+
edits: [{
|
|
747
|
+
oldText: "def old_function():",
|
|
748
|
+
newText: "def new_function():"
|
|
749
|
+
}],
|
|
750
|
+
matchingStrategy: "flexible" // Python indentation may vary
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
path: "README.md",
|
|
754
|
+
edits: [{
|
|
755
|
+
oldText: "## Old Section",
|
|
756
|
+
newText: "## New Section"
|
|
757
|
+
}],
|
|
758
|
+
matchingStrategy: "auto" // Let AI decide best strategy
|
|
759
|
+
}
|
|
760
|
+
],
|
|
761
|
+
failFast: false // Continue even if some files fail
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
528
765
|
---
|
|
529
766
|
|
|
530
767
|
For detailed usage examples, see [Tool Usage Guide](docs/TOOL_USAGE_GUIDE.md)
|
|
@@ -3,7 +3,7 @@ import { ToolSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { promises as fs } from "fs";
|
|
5
5
|
import { WriteFileArgsSchema, WriteMultipleFilesArgsSchema, EditFileArgsSchema, } from "../types/index.js";
|
|
6
|
-
import { validatePath, writeFileContent, applyFileEdits, } from "../utils/lib.js";
|
|
6
|
+
import { validatePath, writeFileContent, readFileContent, applyFileEdits, } from "../utils/lib.js";
|
|
7
7
|
import { isHTMLContent, convertHTMLToPDF, convertHTMLToDOCX, } from "../utils/html-to-document.js";
|
|
8
8
|
const ToolInputSchema = ToolSchema.shape.inputSchema;
|
|
9
9
|
/**
|
|
@@ -53,6 +53,169 @@ async function writeFileBasedOnExtension(validPath, content) {
|
|
|
53
53
|
await writeFileContent(validPath, content);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
async function processFileEditRequest(request, failOnAmbiguous = true) {
|
|
57
|
+
try {
|
|
58
|
+
const validPath = await validatePath(request.path);
|
|
59
|
+
const result = await applyFileEdits(validPath, request.edits, request.dryRun || false, request.matchingStrategy || "auto", request.failOnAmbiguous !== undefined
|
|
60
|
+
? request.failOnAmbiguous
|
|
61
|
+
: failOnAmbiguous, true // Return metadata
|
|
62
|
+
);
|
|
63
|
+
if (typeof result === "string") {
|
|
64
|
+
throw new Error("Expected metadata but got string result");
|
|
65
|
+
}
|
|
66
|
+
// Aggregate metadata from all edits
|
|
67
|
+
const totalOccurrences = result.metadata.reduce((sum, r) => sum + r.occurrences, 0);
|
|
68
|
+
const usedStrategies = [...new Set(result.metadata.map((r) => r.strategy))];
|
|
69
|
+
const finalStrategy = result.metadata[result.metadata.length - 1]?.strategy || "exact";
|
|
70
|
+
const warning = result.metadata.find((r) => r.warning)?.warning;
|
|
71
|
+
const ambiguity = result.metadata.find((r) => r.ambiguity)?.ambiguity;
|
|
72
|
+
return {
|
|
73
|
+
path: request.path,
|
|
74
|
+
success: true,
|
|
75
|
+
strategy: finalStrategy,
|
|
76
|
+
occurrences: totalOccurrences,
|
|
77
|
+
diff: result.diff,
|
|
78
|
+
dryRun: request.dryRun,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return {
|
|
83
|
+
path: request.path,
|
|
84
|
+
success: false,
|
|
85
|
+
error: error instanceof Error ? error.message : String(error),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function processMultiFileEdits(files, failFast = true) {
|
|
90
|
+
const results = [];
|
|
91
|
+
const rollbackData = [];
|
|
92
|
+
let hasFailures = false;
|
|
93
|
+
try {
|
|
94
|
+
// Process files sequentially if failFast is true, concurrently if false
|
|
95
|
+
if (failFast) {
|
|
96
|
+
// Process sequentially to stop on first failure
|
|
97
|
+
for (const request of files) {
|
|
98
|
+
// For rollback capability, read original content before editing
|
|
99
|
+
let originalContent;
|
|
100
|
+
if (failFast && !request.dryRun) {
|
|
101
|
+
try {
|
|
102
|
+
originalContent = await readFileContent(await validatePath(request.path));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// If we can't read the original content, we can't provide rollback
|
|
106
|
+
// This is acceptable - the file might not exist or be readable
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const result = await processFileEditRequest(request);
|
|
110
|
+
results.push(result);
|
|
111
|
+
// Track successful edits for potential rollback
|
|
112
|
+
if (result.success &&
|
|
113
|
+
!request.dryRun &&
|
|
114
|
+
originalContent !== undefined) {
|
|
115
|
+
rollbackData.push({
|
|
116
|
+
path: request.path,
|
|
117
|
+
originalContent,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (!result.success) {
|
|
121
|
+
hasFailures = true;
|
|
122
|
+
// Rollback all previously successful edits
|
|
123
|
+
await performRollback(rollbackData);
|
|
124
|
+
break; // Stop processing remaining files
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Process all concurrently, collect all results (no rollback for concurrent mode)
|
|
130
|
+
const promises = files.map((request) => processFileEditRequest(request));
|
|
131
|
+
const allResults = await Promise.allSettled(promises);
|
|
132
|
+
for (let i = 0; i < allResults.length; i++) {
|
|
133
|
+
const settled = allResults[i];
|
|
134
|
+
const request = files[i];
|
|
135
|
+
if (settled.status === "fulfilled") {
|
|
136
|
+
results.push(settled.value);
|
|
137
|
+
if (!settled.value.success)
|
|
138
|
+
hasFailures = true;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Handle unexpected promise rejections
|
|
142
|
+
results.push({
|
|
143
|
+
path: request.path,
|
|
144
|
+
success: false,
|
|
145
|
+
error: `Unexpected error: ${settled.reason}`,
|
|
146
|
+
});
|
|
147
|
+
hasFailures = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
// If there's an unexpected error during processing, attempt rollback
|
|
154
|
+
if (failFast && rollbackData.length > 0) {
|
|
155
|
+
await performRollback(rollbackData);
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
results,
|
|
161
|
+
summary: {
|
|
162
|
+
total: files.length,
|
|
163
|
+
successful: results.filter((r) => r.success).length,
|
|
164
|
+
failed: results.filter((r) => !r.success).length,
|
|
165
|
+
hasFailures,
|
|
166
|
+
failFast,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function performRollback(rollbackData) {
|
|
171
|
+
for (const item of rollbackData.reverse()) {
|
|
172
|
+
// Rollback in reverse order
|
|
173
|
+
try {
|
|
174
|
+
await writeFileContent(item.path, item.originalContent);
|
|
175
|
+
}
|
|
176
|
+
catch (rollbackError) {
|
|
177
|
+
// Log rollback failure but don't throw - we want to attempt all rollbacks
|
|
178
|
+
console.error(`Failed to rollback ${item.path}: ${rollbackError}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function formatMultiFileEditResults(editResults) {
|
|
183
|
+
let output = "";
|
|
184
|
+
// Summary header
|
|
185
|
+
output += `Multi-File Edit Summary:\n`;
|
|
186
|
+
output += `Total files: ${editResults.summary.total}\n`;
|
|
187
|
+
output += `Successful: ${editResults.summary.successful}\n`;
|
|
188
|
+
output += `Failed: ${editResults.summary.failed}\n`;
|
|
189
|
+
output += `Mode: ${editResults.summary.failFast ? "failFast (atomic)" : "continueOnError"}\n`;
|
|
190
|
+
if (editResults.summary.failFast && editResults.summary.hasFailures) {
|
|
191
|
+
output += `⚠️ Atomic operation failed - all successful edits were rolled back\n`;
|
|
192
|
+
}
|
|
193
|
+
output += `\n`;
|
|
194
|
+
// Individual file results
|
|
195
|
+
editResults.results.forEach((result, index) => {
|
|
196
|
+
output += `File ${index + 1}: ${result.path}\n`;
|
|
197
|
+
output += `Status: ${result.success ? "✓ SUCCESS" : "✗ FAILED"}\n`;
|
|
198
|
+
if (result.success) {
|
|
199
|
+
if (result.strategy) {
|
|
200
|
+
output += `Strategy: ${result.strategy}\n`;
|
|
201
|
+
}
|
|
202
|
+
if (result.occurrences !== undefined) {
|
|
203
|
+
output += `Occurrences: ${result.occurrences}\n`;
|
|
204
|
+
}
|
|
205
|
+
if (result.dryRun) {
|
|
206
|
+
output += `Mode: DRY RUN (no changes made)\n`;
|
|
207
|
+
}
|
|
208
|
+
if (result.diff) {
|
|
209
|
+
output += `\n${result.diff}\n`;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
output += `Error: ${result.error}\n`;
|
|
214
|
+
}
|
|
215
|
+
output += "\n" + "=".repeat(50) + "\n\n";
|
|
216
|
+
});
|
|
217
|
+
return output.trim();
|
|
218
|
+
}
|
|
56
219
|
export function getWriteTools() {
|
|
57
220
|
return [
|
|
58
221
|
{
|
|
@@ -80,31 +243,34 @@ export function getWriteTools() {
|
|
|
80
243
|
},
|
|
81
244
|
{
|
|
82
245
|
name: "edit_file",
|
|
83
|
-
description: "Apply precise modifications to text and code files with intelligent matching
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"\n\n" +
|
|
90
|
-
"Matching Strategies (in order when using 'auto'):\n" +
|
|
246
|
+
description: "Apply precise modifications to text and code files with intelligent matching.\n\n" +
|
|
247
|
+
"**Single File Editing (mode: 'single'):**\n" +
|
|
248
|
+
"Edit one file with multiple sequential edits using exact, flexible, or fuzzy matching strategies.\n\n" +
|
|
249
|
+
"**Multi-File Editing (mode: 'multiple'):**\n" +
|
|
250
|
+
"Edit multiple files concurrently in a single operation. Each file can have its own edit configuration.\n\n" +
|
|
251
|
+
"**Matching Strategies:**\n" +
|
|
91
252
|
"1. Exact: Character-for-character match (fastest, safest)\n" +
|
|
92
253
|
"2. Flexible: Whitespace-insensitive, preserves original indentation\n" +
|
|
93
|
-
"3. Fuzzy: Token-based regex matching for maximum compatibility\n" +
|
|
94
|
-
"
|
|
95
|
-
"
|
|
254
|
+
"3. Fuzzy: Token-based regex matching for maximum compatibility\n\n" +
|
|
255
|
+
"**Features:**\n" +
|
|
256
|
+
"- Concurrent processing for multi-file operations\n" +
|
|
257
|
+
"- Per-file matching strategy control\n" +
|
|
258
|
+
"- Dry-run preview mode\n" +
|
|
259
|
+
"- Detailed diff output with statistics\n" +
|
|
260
|
+
"- Atomic operations with rollback capability\n" +
|
|
261
|
+
"- Cross-platform line ending preservation\n\n" +
|
|
262
|
+
"**Maximum:** 50 files per multi-file operation\n\n" +
|
|
263
|
+
"**Best Practices:**\n" +
|
|
96
264
|
"- Include 3-5 lines of context before and after the change for reliability\n" +
|
|
97
265
|
"- Add 'instruction' field to describe the purpose of each edit\n" +
|
|
98
266
|
"- Use 'dryRun: true' to preview changes before applying\n" +
|
|
99
267
|
"- For multiple related changes, use array of edits (applied sequentially)\n" +
|
|
100
268
|
"- Set 'expectedOccurrences' to validate replacement count\n" +
|
|
101
|
-
"- Use 'matchingStrategy' to control matching behavior (defaults to 'auto')\n" +
|
|
102
|
-
"
|
|
103
|
-
"CRITICAL - Multi-line Content:\n" +
|
|
269
|
+
"- Use 'matchingStrategy' to control matching behavior (defaults to 'auto')\n\n" +
|
|
270
|
+
"**CRITICAL - Multi-line Content:**\n" +
|
|
104
271
|
"- Use actual newline characters in oldText/newText strings, NOT \\n escape sequences\n" +
|
|
105
272
|
"- The MCP/JSON layer handles encoding automatically\n" +
|
|
106
|
-
"- Using \\n literally will search for/write backslash+n characters (wrong!)\n" +
|
|
107
|
-
"\n" +
|
|
273
|
+
"- Using \\n literally will search for/write backslash+n characters (wrong!)\n\n" +
|
|
108
274
|
"Only works within allowed directories.",
|
|
109
275
|
inputSchema: zodToJsonSchema(EditFileArgsSchema),
|
|
110
276
|
},
|
|
@@ -146,11 +312,41 @@ export async function handleWriteTool(name, args) {
|
|
|
146
312
|
if (!parsed.success) {
|
|
147
313
|
throw new Error(`Invalid arguments for edit_file: ${parsed.error}`);
|
|
148
314
|
}
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
315
|
+
// Determine mode and route to appropriate handler
|
|
316
|
+
const mode = parsed.data.mode || "single";
|
|
317
|
+
if (mode === "single") {
|
|
318
|
+
// Single file mode (backward compatible)
|
|
319
|
+
if (!parsed.data.path || !parsed.data.edits) {
|
|
320
|
+
throw new Error("Single mode requires 'path' and 'edits' fields");
|
|
321
|
+
}
|
|
322
|
+
const result = await processFileEditRequest({
|
|
323
|
+
path: parsed.data.path,
|
|
324
|
+
edits: parsed.data.edits,
|
|
325
|
+
matchingStrategy: parsed.data.matchingStrategy,
|
|
326
|
+
dryRun: parsed.data.dryRun,
|
|
327
|
+
failOnAmbiguous: parsed.data.failOnAmbiguous,
|
|
328
|
+
});
|
|
329
|
+
if (!result.success) {
|
|
330
|
+
throw new Error(result.error);
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
content: [{ type: "text", text: result.diff }],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
else if (mode === "multiple") {
|
|
337
|
+
// Multi-file mode
|
|
338
|
+
if (!parsed.data.files) {
|
|
339
|
+
throw new Error("Multiple mode requires 'files' field");
|
|
340
|
+
}
|
|
341
|
+
const editResults = await processMultiFileEdits(parsed.data.files, parsed.data.failFast);
|
|
342
|
+
const output = formatMultiFileEditResults(editResults);
|
|
343
|
+
return {
|
|
344
|
+
content: [{ type: "text", text: output }],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
throw new Error(`Invalid mode: ${mode}`);
|
|
349
|
+
}
|
|
154
350
|
}
|
|
155
351
|
case "write_multiple_files": {
|
|
156
352
|
const parsed = WriteMultipleFilesArgsSchema.safeParse(args);
|
package/dist/types/index.js
CHANGED
|
@@ -192,17 +192,66 @@ export const EditOperation = z.object({
|
|
|
192
192
|
"Set to a specific number to validate the replacement count. " +
|
|
193
193
|
"The tool will fail if actual occurrences don't match this number."),
|
|
194
194
|
});
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
// Single file edit request (for multi-file operations)
|
|
196
|
+
export const EditFileRequestSchema = z.object({
|
|
197
|
+
path: z.string().describe("Path to the file to edit"),
|
|
197
198
|
edits: z
|
|
198
199
|
.array(EditOperation)
|
|
199
200
|
.min(1, "At least one edit must be provided")
|
|
200
|
-
.describe("Array of edits to apply
|
|
201
|
+
.describe("Array of edits to apply to this file"),
|
|
202
|
+
matchingStrategy: z
|
|
203
|
+
.enum(["exact", "flexible", "fuzzy", "auto"])
|
|
204
|
+
.optional()
|
|
205
|
+
.default("auto")
|
|
206
|
+
.describe("Matching strategy for this file:\n" +
|
|
207
|
+
"- 'exact': Strict character-for-character match (fastest, safest)\n" +
|
|
208
|
+
"- 'flexible': Whitespace-insensitive line-by-line matching\n" +
|
|
209
|
+
"- 'fuzzy': Token-based regex matching (most permissive)\n" +
|
|
210
|
+
"- 'auto': Try exact → flexible → fuzzy (recommended, default)"),
|
|
201
211
|
dryRun: z
|
|
202
212
|
.boolean()
|
|
203
213
|
.optional()
|
|
204
214
|
.default(false)
|
|
205
|
-
.describe("Preview changes
|
|
215
|
+
.describe("Preview changes for this file without writing"),
|
|
216
|
+
failOnAmbiguous: z
|
|
217
|
+
.boolean()
|
|
218
|
+
.optional()
|
|
219
|
+
.default(true)
|
|
220
|
+
.describe("If true, fail when oldText matches multiple locations (unless expectedOccurrences > 1). " +
|
|
221
|
+
"If false, replace first occurrence only and warn about ambiguity."),
|
|
222
|
+
});
|
|
223
|
+
// Enhanced edit file schema (supports both single and multi-file with explicit mode)
|
|
224
|
+
export const EditFileArgsSchema = z
|
|
225
|
+
.object({
|
|
226
|
+
// Mode discriminator
|
|
227
|
+
mode: z
|
|
228
|
+
.enum(["single", "multiple"])
|
|
229
|
+
.optional()
|
|
230
|
+
.default("single")
|
|
231
|
+
.describe("Edit mode: 'single' for one file, 'multiple' for batch editing"),
|
|
232
|
+
// Single file fields (required when mode is "single")
|
|
233
|
+
path: z
|
|
234
|
+
.string()
|
|
235
|
+
.optional()
|
|
236
|
+
.describe("Path to file (required for single mode)"),
|
|
237
|
+
edits: z
|
|
238
|
+
.array(EditOperation)
|
|
239
|
+
.min(1)
|
|
240
|
+
.optional()
|
|
241
|
+
.describe("Array of edits to apply"),
|
|
242
|
+
// Multi-file fields (required when mode is "multiple")
|
|
243
|
+
files: z
|
|
244
|
+
.array(EditFileRequestSchema)
|
|
245
|
+
.min(1, "At least one file must be provided")
|
|
246
|
+
.max(50, "Maximum 50 files per operation")
|
|
247
|
+
.optional()
|
|
248
|
+
.describe("Array of file edit requests (required for multiple mode)"),
|
|
249
|
+
failFast: z
|
|
250
|
+
.boolean()
|
|
251
|
+
.optional()
|
|
252
|
+
.default(true)
|
|
253
|
+
.describe("Stop processing on first file failure (true) or continue with remaining files (false)"),
|
|
254
|
+
// Global options
|
|
206
255
|
matchingStrategy: z
|
|
207
256
|
.enum(["exact", "flexible", "fuzzy", "auto"])
|
|
208
257
|
.optional()
|
|
@@ -212,13 +261,31 @@ export const EditFileArgsSchema = z.object({
|
|
|
212
261
|
"- 'flexible': Whitespace-insensitive line-by-line matching\n" +
|
|
213
262
|
"- 'fuzzy': Token-based regex matching (most permissive)\n" +
|
|
214
263
|
"- 'auto': Try exact → flexible → fuzzy (recommended, default)"),
|
|
264
|
+
dryRun: z
|
|
265
|
+
.boolean()
|
|
266
|
+
.optional()
|
|
267
|
+
.default(false)
|
|
268
|
+
.describe("Preview changes without writing"),
|
|
215
269
|
failOnAmbiguous: z
|
|
216
270
|
.boolean()
|
|
217
271
|
.optional()
|
|
218
272
|
.default(true)
|
|
219
273
|
.describe("If true, fail when oldText matches multiple locations (unless expectedOccurrences > 1). " +
|
|
220
274
|
"If false, replace first occurrence only and warn about ambiguity."),
|
|
221
|
-
})
|
|
275
|
+
})
|
|
276
|
+
.refine((data) => {
|
|
277
|
+
// Validate based on mode
|
|
278
|
+
if (data.mode === "single") {
|
|
279
|
+
return data.path && data.edits;
|
|
280
|
+
}
|
|
281
|
+
else if (data.mode === "multiple") {
|
|
282
|
+
return data.files;
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}, {
|
|
286
|
+
message: "Invalid configuration: 'path' and 'edits' required for single mode, 'files' required for multiple mode",
|
|
287
|
+
})
|
|
288
|
+
.describe("Edit files with intelligent matching. Supports single file or batch multi-file operations.");
|
|
222
289
|
export const MakeDirectoryArgsSchema = z.object({
|
|
223
290
|
paths: z
|
|
224
291
|
.union([z.string(), z.array(z.string())])
|
package/dist/utils/lib.js
CHANGED
|
@@ -518,7 +518,7 @@ function applyEditWithStrategy(content, edit, strategy, failOnAmbiguous) {
|
|
|
518
518
|
}
|
|
519
519
|
return result;
|
|
520
520
|
}
|
|
521
|
-
export async function applyFileEdits(filePath, edits, dryRun = false, matchingStrategy = "auto", failOnAmbiguous = true) {
|
|
521
|
+
export async function applyFileEdits(filePath, edits, dryRun = false, matchingStrategy = "auto", failOnAmbiguous = true, returnMetadata) {
|
|
522
522
|
// Read file content and detect original line ending
|
|
523
523
|
const rawContent = await fs.readFile(filePath, "utf-8");
|
|
524
524
|
const originalLineEnding = detectLineEnding(rawContent);
|
|
@@ -561,6 +561,12 @@ export async function applyFileEdits(filePath, edits, dryRun = false, matchingSt
|
|
|
561
561
|
throw error;
|
|
562
562
|
}
|
|
563
563
|
}
|
|
564
|
+
if (returnMetadata) {
|
|
565
|
+
return {
|
|
566
|
+
diff: formattedDiff,
|
|
567
|
+
metadata: editResults
|
|
568
|
+
};
|
|
569
|
+
}
|
|
564
570
|
return formattedDiff;
|
|
565
571
|
}
|
|
566
572
|
// Memory-efficient implementation to get the last N lines of a file
|
package/package.json
CHANGED
|
@@ -1,17 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@n0zer0d4y/vulcan-file-ops",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "MCP server that gives Claude Desktop and other AI assistants filesystem superpowers—read, write, edit, and manage files like AI coding assistants",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lloyd Barcatan",
|
|
7
7
|
"homepage": "https://github.com/n0zer0d4y/vulcan-file-ops",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/n0zer0d4y/vulcan-file-ops.git"
|
|
10
|
+
"url": "git+https://github.com/n0zer0d4y/vulcan-file-ops.git"
|
|
11
11
|
},
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/n0zer0d4y/vulcan-file-ops/issues"
|
|
14
14
|
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"mcp-server",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"context-engineering",
|
|
20
|
+
"ai",
|
|
21
|
+
"ai-tools",
|
|
22
|
+
"claude",
|
|
23
|
+
"claude-desktop",
|
|
24
|
+
"filesystem",
|
|
25
|
+
"file-operations",
|
|
26
|
+
"file-management",
|
|
27
|
+
"read-files",
|
|
28
|
+
"write-files",
|
|
29
|
+
"edit-files",
|
|
30
|
+
"ai-assistant",
|
|
31
|
+
"llm",
|
|
32
|
+
"llm-tools",
|
|
33
|
+
"pdf",
|
|
34
|
+
"docx",
|
|
35
|
+
"document-parsing",
|
|
36
|
+
"code-editor",
|
|
37
|
+
"typescript",
|
|
38
|
+
"ai-assisted-coding"
|
|
39
|
+
],
|
|
15
40
|
"type": "module",
|
|
16
41
|
"bin": {
|
|
17
42
|
"vulcan-file-ops": "dist/cli.js"
|