mcp-fs-shell-windows 0.2.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/LICENSE +23 -0
- package/README.md +147 -0
- package/dist/append_files/handler.js +32 -0
- package/dist/append_files/schema.js +7 -0
- package/dist/checksum_files/handler.js +33 -0
- package/dist/checksum_files/schema.js +5 -0
- package/dist/checksum_files_verif/handler.js +66 -0
- package/dist/checksum_files_verif/helpers.js +6 -0
- package/dist/checksum_files_verif/schema.js +8 -0
- package/dist/content_diff/handler.js +11 -0
- package/dist/content_diff/schema.js +7 -0
- package/dist/copy_files/handler.js +43 -0
- package/dist/copy_files/helpers.js +24 -0
- package/dist/copy_files/schema.js +7 -0
- package/dist/count_lines/handler.js +144 -0
- package/dist/count_lines/schema.js +9 -0
- package/dist/create_directories/handler.js +29 -0
- package/dist/create_directories/schema.js +4 -0
- package/dist/delete_files/handler.js +42 -0
- package/dist/delete_files/schema.js +5 -0
- package/dist/delete_files_by_pattern/handler.js +46 -0
- package/dist/delete_files_by_pattern/schema.js +6 -0
- package/dist/directory_tree/handler.js +7 -0
- package/dist/directory_tree/helpers.js +47 -0
- package/dist/directory_tree/schema.js +5 -0
- package/dist/edit_files/handler.js +62 -0
- package/dist/edit_files/schema.js +11 -0
- package/dist/file_diff/handler.js +28 -0
- package/dist/file_diff/schema.js +5 -0
- package/dist/file_info/handler.js +30 -0
- package/dist/file_info/schema.js +4 -0
- package/dist/fuzzy_find_files/handler.js +74 -0
- package/dist/fuzzy_find_files/schema.js +8 -0
- package/dist/helpers/checksum.js +10 -0
- package/dist/helpers/diff.js +10 -0
- package/dist/helpers/path.js +87 -0
- package/dist/index.js +36 -0
- package/dist/list_directory/handler.js +42 -0
- package/dist/list_directory/schema.js +9 -0
- package/dist/move_files/handler.js +68 -0
- package/dist/move_files/schema.js +8 -0
- package/dist/patch_files/handler.js +34 -0
- package/dist/patch_files/helpers.js +103 -0
- package/dist/patch_files/schema.js +18 -0
- package/dist/read_files/handler.js +36 -0
- package/dist/read_files/schema.js +6 -0
- package/dist/search_files/handler.js +15 -0
- package/dist/search_files/helpers.js +71 -0
- package/dist/search_files/schema.js +8 -0
- package/dist/search_glob/handler.js +77 -0
- package/dist/search_glob/schema.js +8 -0
- package/dist/search_regex/handler.js +159 -0
- package/dist/search_regex/schema.js +10 -0
- package/dist/server.js +698 -0
- package/dist/shell/handler.js +687 -0
- package/dist/shell/schema.js +69 -0
- package/dist/write_new_files/handler.js +55 -0
- package/dist/write_new_files/schema.js +9 -0
- package/package.json +37 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
5
|
+
// Import all schemas
|
|
6
|
+
import { ReadFilesArgsSchema } from "./read_files/schema.js";
|
|
7
|
+
import { WriteNewFilesArgsSchema } from "./write_new_files/schema.js";
|
|
8
|
+
import { AppendFilesArgsSchema } from "./append_files/schema.js";
|
|
9
|
+
import { PatchFilesArgsSchema } from "./patch_files/schema.js";
|
|
10
|
+
import { FileDiffArgsSchema } from "./file_diff/schema.js";
|
|
11
|
+
import { ContentDiffArgsSchema } from "./content_diff/schema.js";
|
|
12
|
+
import { DeleteFilesArgsSchema } from "./delete_files/schema.js";
|
|
13
|
+
import { CopyFileArgsSchema } from "./copy_files/schema.js";
|
|
14
|
+
import { MoveFilesArgsSchema } from "./move_files/schema.js";
|
|
15
|
+
import { CreateDirectoriesArgsSchema } from "./create_directories/schema.js";
|
|
16
|
+
import { ListDirectoryArgsSchema } from "./list_directory/schema.js";
|
|
17
|
+
import { DirectoryTreeArgsSchema } from "./directory_tree/schema.js";
|
|
18
|
+
import { SearchFilesArgsSchema } from "./search_files/schema.js";
|
|
19
|
+
import { SearchRegexArgsSchema } from "./search_regex/schema.js";
|
|
20
|
+
import { SearchGlobArgsSchema } from "./search_glob/schema.js";
|
|
21
|
+
import { CountLinesArgsSchema } from "./count_lines/schema.js";
|
|
22
|
+
import { ChecksumFilesArgsSchema } from "./checksum_files/schema.js";
|
|
23
|
+
import { ChecksumFilesVerifArgsSchema } from "./checksum_files_verif/schema.js";
|
|
24
|
+
import { GetFileInfoArgsSchema } from "./file_info/schema.js";
|
|
25
|
+
import { EditFilesArgsSchema } from "./edit_files/schema.js";
|
|
26
|
+
import { FuzzyFindFilesArgsSchema } from "./fuzzy_find_files/schema.js";
|
|
27
|
+
import { DeleteFilesByPatternArgsSchema } from "./delete_files_by_pattern/schema.js";
|
|
28
|
+
import { ShellRunArgsSchema } from "./shell/schema.js";
|
|
29
|
+
import { ShellTestArgsSchema } from "./shell/schema.js";
|
|
30
|
+
import { ShellStartArgsSchema } from "./shell/schema.js";
|
|
31
|
+
import { ShellCheckArgsSchema } from "./shell/schema.js";
|
|
32
|
+
import { ShellCancelArgsSchema } from "./shell/schema.js";
|
|
33
|
+
import { ShellTerminalArgsSchema } from "./shell/schema.js";
|
|
34
|
+
import { ShellPythonArgsSchema } from "./shell/schema.js";
|
|
35
|
+
import { ShellCwdArgsSchema } from "./shell/schema.js";
|
|
36
|
+
// Import all handlers
|
|
37
|
+
import { handleReadFiles } from "./read_files/handler.js";
|
|
38
|
+
import { handleWriteNewFiles } from "./write_new_files/handler.js";
|
|
39
|
+
import { handleAppendFiles } from "./append_files/handler.js";
|
|
40
|
+
import { handlePatchFiles } from "./patch_files/handler.js";
|
|
41
|
+
import { handleFileDiff } from "./file_diff/handler.js";
|
|
42
|
+
import { handleContentDiff } from "./content_diff/handler.js";
|
|
43
|
+
import { handleDeleteFiles } from "./delete_files/handler.js";
|
|
44
|
+
import { handleCopyFile } from "./copy_files/handler.js";
|
|
45
|
+
import { handleMoveFiles } from "./move_files/handler.js";
|
|
46
|
+
import { handleCreateDirectories } from "./create_directories/handler.js";
|
|
47
|
+
import { handleListDirectory } from "./list_directory/handler.js";
|
|
48
|
+
import { handleDirectoryTree } from "./directory_tree/handler.js";
|
|
49
|
+
import { handleSearchFiles } from "./search_files/handler.js";
|
|
50
|
+
import { handleSearchRegex } from "./search_regex/handler.js";
|
|
51
|
+
import { handleSearchGlob } from "./search_glob/handler.js";
|
|
52
|
+
import { handleCountLines } from "./count_lines/handler.js";
|
|
53
|
+
import { handleChecksumFiles } from "./checksum_files/handler.js";
|
|
54
|
+
import { handleChecksumFilesVerif } from "./checksum_files_verif/handler.js";
|
|
55
|
+
import { handleGetFileInfo } from "./file_info/handler.js";
|
|
56
|
+
import { handleEditFiles } from "./edit_files/handler.js";
|
|
57
|
+
import { handleFuzzyFindFiles } from "./fuzzy_find_files/handler.js";
|
|
58
|
+
import { handleDeleteFilesByPattern } from "./delete_files_by_pattern/handler.js";
|
|
59
|
+
import { handleShellRun } from "./shell/handler.js";
|
|
60
|
+
import { handleShellTest } from "./shell/handler.js";
|
|
61
|
+
import { handleShellStart } from "./shell/handler.js";
|
|
62
|
+
import { handleShellCheck } from "./shell/handler.js";
|
|
63
|
+
import { handleShellCancel } from "./shell/handler.js";
|
|
64
|
+
import { handleShellTerminal } from "./shell/handler.js";
|
|
65
|
+
import { handleShellPython } from "./shell/handler.js";
|
|
66
|
+
import { handleShellCwd } from "./shell/handler.js";
|
|
67
|
+
const ToolInputSchema = ToolSchema.shape.inputSchema;
|
|
68
|
+
export class FilesystemServer {
|
|
69
|
+
server;
|
|
70
|
+
allowedDirectories;
|
|
71
|
+
constructor(allowedDirectories) {
|
|
72
|
+
this.allowedDirectories = allowedDirectories;
|
|
73
|
+
this.server = new Server({
|
|
74
|
+
name: "secure-filesystem-server",
|
|
75
|
+
version: "0.2.4",
|
|
76
|
+
}, {
|
|
77
|
+
capabilities: {
|
|
78
|
+
tools: {},
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
this.setupRequestHandlers();
|
|
82
|
+
}
|
|
83
|
+
setupRequestHandlers() {
|
|
84
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
85
|
+
return {
|
|
86
|
+
tools: [
|
|
87
|
+
{
|
|
88
|
+
name: "read_files",
|
|
89
|
+
description: "Read multiple files simultaneously. Works on a single file too. " +
|
|
90
|
+
"Failed reads won't stop the entire operation. " +
|
|
91
|
+
"Each line is prefixed with its line number (format: \"1: line content\"). " +
|
|
92
|
+
"Each file's content is returned with its path as a reference. " +
|
|
93
|
+
"Use offset (1-indexed start line) and/or limit (max lines) to read a range of a large file. " +
|
|
94
|
+
"Only works within allowed directories.",
|
|
95
|
+
inputSchema: zodToJsonSchema(ReadFilesArgsSchema),
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "write_new_files",
|
|
99
|
+
description: "Create multiple new files in a single operation. Works on a single file too. " +
|
|
100
|
+
"Fails if a file already exists unless its overwrite flag is true. " +
|
|
101
|
+
"Handles text content with UTF-8 encoding, or base64 content for binary files (encoding: \"base64\"). " +
|
|
102
|
+
"Parent directories are created as needed. " +
|
|
103
|
+
"Partial failures won't stop the entire operation. " +
|
|
104
|
+
"Only works within allowed directories.",
|
|
105
|
+
inputSchema: zodToJsonSchema(WriteNewFilesArgsSchema),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "append_files",
|
|
109
|
+
description: "Append content to multiple existing files without overwriting. Works on a single file too. " +
|
|
110
|
+
"Creates files if they don't exist. " +
|
|
111
|
+
"Handles text content with UTF-8 encoding. " +
|
|
112
|
+
"Partial failures won't stop the entire operation. " +
|
|
113
|
+
"Only works within allowed directories.",
|
|
114
|
+
inputSchema: zodToJsonSchema(AppendFilesArgsSchema),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "delete_files",
|
|
118
|
+
description: "Delete multiple files or directories in a single operation. Works on a single file too. " +
|
|
119
|
+
"Set recursive flag to true to delete directories with contents. " +
|
|
120
|
+
"Partial failures won't stop the entire operation. " +
|
|
121
|
+
"Use with caution as operation is permanent. " +
|
|
122
|
+
"Only works within allowed directories.",
|
|
123
|
+
inputSchema: zodToJsonSchema(DeleteFilesArgsSchema),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "copy_file",
|
|
127
|
+
description: "Copy files or directories. " +
|
|
128
|
+
"Set recursive flag to true to copy directories with contents. " +
|
|
129
|
+
"Set overwrite flag to true to replace existing destinations. " +
|
|
130
|
+
"Both source and destination must be within allowed directories.",
|
|
131
|
+
inputSchema: zodToJsonSchema(CopyFileArgsSchema),
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "file_diff",
|
|
135
|
+
description: "Compare contents of two files and show differences. " +
|
|
136
|
+
"Returns a unified diff showing all changes between files. " +
|
|
137
|
+
"Only works within allowed directories.",
|
|
138
|
+
inputSchema: zodToJsonSchema(FileDiffArgsSchema),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "content_diff",
|
|
142
|
+
description: "Compare two text strings and show differences. " +
|
|
143
|
+
"Returns a unified diff showing all changes between the contents. " +
|
|
144
|
+
"Useful for comparing snippets without saving them to disk. " +
|
|
145
|
+
"Custom labels can be provided for each content.",
|
|
146
|
+
inputSchema: zodToJsonSchema(ContentDiffArgsSchema),
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "patch_files",
|
|
150
|
+
description: "Patch multiple files simultaneously using line numbers. Works on a single file too. " +
|
|
151
|
+
"Each patch has an action: replace (default, replace lines startLine-endLine with newText), insertBefore (insert newText before startLine; startLine = total lines + 1 appends to the end), or delete (remove lines startLine-endLine). " +
|
|
152
|
+
"All patches reference the original line numbers. " +
|
|
153
|
+
"Shows git-style diffs for all changes (dryRun: true previews without writing). " +
|
|
154
|
+
"Note: files are written back with LF line endings. " +
|
|
155
|
+
"Partial failures won't stop the entire operation. " +
|
|
156
|
+
"Only works within allowed directories.",
|
|
157
|
+
inputSchema: zodToJsonSchema(PatchFilesArgsSchema),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "create_directories",
|
|
161
|
+
description: "Create multiple directory paths in one operation. Works on a single directory too. " +
|
|
162
|
+
"Creates parent directories if needed. " +
|
|
163
|
+
"Succeeds silently if directories exist. " +
|
|
164
|
+
"Partial failures won't stop the entire operation. " +
|
|
165
|
+
"Only works within allowed directories.",
|
|
166
|
+
inputSchema: zodToJsonSchema(CreateDirectoriesArgsSchema),
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: "list_directory",
|
|
170
|
+
description: "List directory contents with [FILE] or [DIR] prefixes (single level). " +
|
|
171
|
+
"Entries are sorted by name and paginated with offset/limit (default limit 100). " +
|
|
172
|
+
"File sizes are shown by default (includeSizes: false to omit). " +
|
|
173
|
+
"Filter with type (files/directories/all) and ignore glob patterns. " +
|
|
174
|
+
"Use directory_tree for recursive views. " +
|
|
175
|
+
"Only works within allowed directories.",
|
|
176
|
+
inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "directory_tree",
|
|
180
|
+
description: "Get a recursive tree view of files and directories as a JSON structure. " +
|
|
181
|
+
"Returns JSON with name, type, and children properties. " +
|
|
182
|
+
"Supports excluding files/directories using glob patterns. " +
|
|
183
|
+
"Only works within allowed directories.",
|
|
184
|
+
inputSchema: zodToJsonSchema(DirectoryTreeArgsSchema),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "search_files",
|
|
188
|
+
description: "Recursively search for files/directories by name substring. " +
|
|
189
|
+
"Case-insensitive matching. " +
|
|
190
|
+
"Supports exclude patterns with glob format. " +
|
|
191
|
+
"Returns full paths to matches, paginated with offset/maxResults (default 500). " +
|
|
192
|
+
"Only searches within allowed directories.",
|
|
193
|
+
inputSchema: zodToJsonSchema(SearchFilesArgsSchema),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "search_regex",
|
|
197
|
+
description: "Search file contents using regular expressions. " +
|
|
198
|
+
"path can be a directory (recursively searched) or a single file. " +
|
|
199
|
+
"Supports filtering files by patterns. " +
|
|
200
|
+
"Returns matching lines with line numbers, paginated with offset/maxResults (default 100). " +
|
|
201
|
+
"Only searches within allowed directories.",
|
|
202
|
+
inputSchema: zodToJsonSchema(SearchRegexArgsSchema),
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "search_glob",
|
|
206
|
+
description: "Find files using glob patterns. " +
|
|
207
|
+
"Supports powerful patterns like '**/*.js' or 'src/**/*.{ts,tsx}'. " +
|
|
208
|
+
"Excludes can be specified to filter results. " +
|
|
209
|
+
"Returns full paths to matching files, paginated with offset/maxResults (default 500). " +
|
|
210
|
+
"Only searches within allowed directories.",
|
|
211
|
+
inputSchema: zodToJsonSchema(SearchGlobArgsSchema),
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: "count_lines",
|
|
215
|
+
description: "Count lines in files with optional pattern matching. " +
|
|
216
|
+
"Can recursively count lines in multiple files. " +
|
|
217
|
+
"Filter lines with regex patterns or skip empty lines. " +
|
|
218
|
+
"Returns counts by file and totals. " +
|
|
219
|
+
"Only works within allowed directories.",
|
|
220
|
+
inputSchema: zodToJsonSchema(CountLinesArgsSchema),
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "checksum_files",
|
|
224
|
+
description: "Generate checksums for multiple files. Works on a single file too. " +
|
|
225
|
+
"Supports md5, sha1, sha256, and sha512 algorithms. " +
|
|
226
|
+
"Returns hashes for all files in a consistent format. " +
|
|
227
|
+
"Only works within allowed directories.",
|
|
228
|
+
inputSchema: zodToJsonSchema(ChecksumFilesArgsSchema),
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "checksum_files_verif",
|
|
232
|
+
description: "Verify multiple files against expected checksums. Works on a single file too. " +
|
|
233
|
+
"Supports md5, sha1, sha256, and sha512 algorithms. " +
|
|
234
|
+
"Returns verification results for all files. " +
|
|
235
|
+
"Only works within allowed directories.",
|
|
236
|
+
inputSchema: zodToJsonSchema(ChecksumFilesVerifArgsSchema),
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "get_file_info",
|
|
240
|
+
description: "Get detailed file/directory metadata. " +
|
|
241
|
+
"Returns size, creation time, modified time, access time, type, and permissions. " +
|
|
242
|
+
"Only works within allowed directories.",
|
|
243
|
+
inputSchema: zodToJsonSchema(GetFileInfoArgsSchema),
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "list_allowed_directories",
|
|
247
|
+
description: "List all directories the server is allowed to access. " +
|
|
248
|
+
"No input required. " +
|
|
249
|
+
"Returns directories that this server can read/write from.",
|
|
250
|
+
inputSchema: {
|
|
251
|
+
type: "object",
|
|
252
|
+
properties: {},
|
|
253
|
+
required: [],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "edit_files",
|
|
258
|
+
description: "Edit files using exact text replacement. Works on a single file too. " +
|
|
259
|
+
"Each edit specifies an oldString (exact literal text) and a newString. Edits apply in order. " +
|
|
260
|
+
"By default each oldString must match exactly once (set replaceAll: true to replace all occurrences). " +
|
|
261
|
+
"Line endings are handled automatically: \\n in strings matches CRLF files, and the file's dominant line ending is preserved on write. " +
|
|
262
|
+
"A failed edit aborts that file before anything is written. " +
|
|
263
|
+
"Only works within allowed directories.",
|
|
264
|
+
inputSchema: zodToJsonSchema(EditFilesArgsSchema),
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: "fuzzy_find_files",
|
|
268
|
+
description: "Fuzzy find files by name/path similarity (Levenshtein distance). " +
|
|
269
|
+
"Typo-tolerant; for substring matching use search_files. " +
|
|
270
|
+
"Returns the best matches with their distance scores. " +
|
|
271
|
+
"Only works within allowed directories.",
|
|
272
|
+
inputSchema: zodToJsonSchema(FuzzyFindFilesArgsSchema),
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "delete_files_by_pattern",
|
|
276
|
+
description: "Delete entries in a single directory whose names match a regular expression (e.g. '^temp_.*\\.txt$'). " +
|
|
277
|
+
"Subdirectories are not scanned. Directories are only deleted when includeDirectories is true. " +
|
|
278
|
+
"Use with caution as the operation is permanent. " +
|
|
279
|
+
"Only works within allowed directories.",
|
|
280
|
+
inputSchema: zodToJsonSchema(DeleteFilesByPatternArgsSchema),
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "transfer_files",
|
|
284
|
+
description: "Move or rename multiple files and directories in a single operation. Works on a single file too. " +
|
|
285
|
+
"By default fails if any destination exists, use overwrite flag to force. " +
|
|
286
|
+
"Creates parent directories of destinations if needed. " +
|
|
287
|
+
"Partial failures won't stop the entire operation. " +
|
|
288
|
+
"All sources and destinations must be within allowed directories.",
|
|
289
|
+
inputSchema: zodToJsonSchema(MoveFilesArgsSchema),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "shell_run",
|
|
293
|
+
description: "Execute a shell command synchronously and return exit code, stdout, and stderr. " +
|
|
294
|
+
"Host is Windows; use cmd.exe syntax (e.g. `dir C:\\`, `powershell -NoProfile -Command ...`). " +
|
|
295
|
+
"Optional input is piped to the command's stdin. " +
|
|
296
|
+
"Optional cwd overrides the default working directory (see shell_cwd). " +
|
|
297
|
+
"Bounded: default 5 s timeout, max 28 s - for anything longer use shell_start (background job) and poll with shell_check. " +
|
|
298
|
+
"Non-zero exit or timeout returns an error that still includes the captured output.",
|
|
299
|
+
inputSchema: zodToJsonSchema(ShellRunArgsSchema),
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "shell_test",
|
|
303
|
+
description: "Execute a test command (e.g. `npm test`, `pytest`) and return {command, exit_code, stdout, stderr, passed}. " +
|
|
304
|
+
"Never errors on a failing test. Runs with CI=true. " +
|
|
305
|
+
"Host is Windows; use cmd.exe syntax. " +
|
|
306
|
+
"Bounded to 28 s by the MCP client timeout - for longer suites use shell_start + shell_check.",
|
|
307
|
+
inputSchema: zodToJsonSchema(ShellTestArgsSchema),
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: "shell_start",
|
|
311
|
+
description: "Start a long-running shell command as a background job and return immediately with a job ID. " +
|
|
312
|
+
"Host is Windows; use cmd.exe syntax. " +
|
|
313
|
+
"The job keeps running inside this MCP server process; output is captured (last 256 KB of stdout/stderr) and appended to a log file. " +
|
|
314
|
+
"Auto-killed after timeout_hours (default 10, max 10). " +
|
|
315
|
+
"Poll with shell_check; kill with shell_cancel. " +
|
|
316
|
+
"Optional cwd overrides the default working directory (see shell_cwd).",
|
|
317
|
+
inputSchema: zodToJsonSchema(ShellStartArgsSchema),
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "shell_check",
|
|
321
|
+
description: "Check the status and output of a shell background job started with shell_start (or a shell_terminal window). " +
|
|
322
|
+
"Returns {id, name, status, exitCode, duration_seconds, stdout_tail, stderr_tail, logFile}. " +
|
|
323
|
+
"Status values: running, completed, error, cancelled, timeout.",
|
|
324
|
+
inputSchema: zodToJsonSchema(ShellCheckArgsSchema),
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "shell_cancel",
|
|
328
|
+
description: "Kill a running shell job (from shell_start or shell_terminal) by job ID. " +
|
|
329
|
+
"Kills the whole process tree.",
|
|
330
|
+
inputSchema: zodToJsonSchema(ShellCancelArgsSchema),
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: "shell_terminal",
|
|
334
|
+
description: "Open a visible, separate, interactive cmd.exe console window (Windows) that runs the given command (as a batch file) with cmd /k. " +
|
|
335
|
+
"The window has its own console (the user can type into it) and persists after the command finishes. " +
|
|
336
|
+
"Use it for interactive sessions (ssh, REPLs, TUI programs, prompted installers) - the one case shell_start cannot cover. " +
|
|
337
|
+
"Returns a job ID with the window's PID; close the window (kill the whole tree) with shell_cancel. " +
|
|
338
|
+
"Optional cwd sets the window's starting directory.",
|
|
339
|
+
inputSchema: zodToJsonSchema(ShellTerminalArgsSchema),
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: "shell_python",
|
|
343
|
+
description: "Execute a Python code snippet (requires system Python on PATH). " +
|
|
344
|
+
"Writes the code to a temp .py file and runs it. " +
|
|
345
|
+
"Returns exit code, stdout, stderr. " +
|
|
346
|
+
"Default 5 s timeout, max 28 s; optional cwd. " +
|
|
347
|
+
"Non-zero exit returns an error that still includes the captured output.",
|
|
348
|
+
inputSchema: zodToJsonSchema(ShellPythonArgsSchema),
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "shell_cwd",
|
|
352
|
+
description: "Get or set the default working directory used by the other shell_* tools. " +
|
|
353
|
+
"With no argument returns the current default; with path sets it (must be an existing directory). " +
|
|
354
|
+
"The default starts as the MCP server process's CWD and resets when the server restarts.",
|
|
355
|
+
inputSchema: zodToJsonSchema(ShellCwdArgsSchema),
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
};
|
|
359
|
+
});
|
|
360
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
361
|
+
try {
|
|
362
|
+
const { name, arguments: args } = request.params;
|
|
363
|
+
switch (name) {
|
|
364
|
+
case "read_files": {
|
|
365
|
+
const parsed = ReadFilesArgsSchema.safeParse(args);
|
|
366
|
+
if (!parsed.success) {
|
|
367
|
+
throw new Error(`Invalid arguments for read_files: ${parsed.error}`);
|
|
368
|
+
}
|
|
369
|
+
const result = await handleReadFiles(parsed.data.paths, this.allowedDirectories, parsed.data.offset, parsed.data.limit);
|
|
370
|
+
return {
|
|
371
|
+
content: [{ type: "text", text: result }],
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
case "write_new_files": {
|
|
375
|
+
const parsed = WriteNewFilesArgsSchema.safeParse(args);
|
|
376
|
+
if (!parsed.success) {
|
|
377
|
+
throw new Error(`Invalid arguments for write_new_files: ${parsed.error}`);
|
|
378
|
+
}
|
|
379
|
+
const result = await handleWriteNewFiles(parsed.data.files, this.allowedDirectories);
|
|
380
|
+
return {
|
|
381
|
+
content: [{ type: "text", text: result }],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
case "append_files": {
|
|
385
|
+
const parsed = AppendFilesArgsSchema.safeParse(args);
|
|
386
|
+
if (!parsed.success) {
|
|
387
|
+
throw new Error(`Invalid arguments for append_files: ${parsed.error}`);
|
|
388
|
+
}
|
|
389
|
+
const result = await handleAppendFiles(parsed.data.files, this.allowedDirectories);
|
|
390
|
+
return {
|
|
391
|
+
content: [{ type: "text", text: result }],
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
case "delete_files": {
|
|
395
|
+
const parsed = DeleteFilesArgsSchema.safeParse(args);
|
|
396
|
+
if (!parsed.success) {
|
|
397
|
+
throw new Error(`Invalid arguments for delete_files: ${parsed.error}`);
|
|
398
|
+
}
|
|
399
|
+
const result = await handleDeleteFiles(parsed.data.paths, parsed.data.recursive, this.allowedDirectories);
|
|
400
|
+
return {
|
|
401
|
+
content: [{ type: "text", text: result }],
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
case "copy_file": {
|
|
405
|
+
const parsed = CopyFileArgsSchema.safeParse(args);
|
|
406
|
+
if (!parsed.success) {
|
|
407
|
+
throw new Error(`Invalid arguments for copy_file: ${parsed.error}`);
|
|
408
|
+
}
|
|
409
|
+
const result = await handleCopyFile(parsed.data.source, parsed.data.destination, parsed.data.recursive, parsed.data.overwrite, this.allowedDirectories);
|
|
410
|
+
return {
|
|
411
|
+
content: [{ type: "text", text: result }],
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
case "file_diff": {
|
|
415
|
+
const parsed = FileDiffArgsSchema.safeParse(args);
|
|
416
|
+
if (!parsed.success) {
|
|
417
|
+
throw new Error(`Invalid arguments for file_diff: ${parsed.error}`);
|
|
418
|
+
}
|
|
419
|
+
const result = await handleFileDiff(parsed.data.file1, parsed.data.file2, this.allowedDirectories);
|
|
420
|
+
return {
|
|
421
|
+
content: [{ type: "text", text: result }],
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
case "content_diff": {
|
|
425
|
+
const parsed = ContentDiffArgsSchema.safeParse(args);
|
|
426
|
+
if (!parsed.success) {
|
|
427
|
+
throw new Error(`Invalid arguments for content_diff: ${parsed.error}`);
|
|
428
|
+
}
|
|
429
|
+
const result = await handleContentDiff(parsed.data.content1, parsed.data.content2, parsed.data.label1, parsed.data.label2);
|
|
430
|
+
return {
|
|
431
|
+
content: [{ type: "text", text: result }],
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
case "patch_files": {
|
|
435
|
+
const parsed = PatchFilesArgsSchema.safeParse(args);
|
|
436
|
+
if (!parsed.success) {
|
|
437
|
+
throw new Error(`Invalid arguments for patch_files: ${parsed.error}`);
|
|
438
|
+
}
|
|
439
|
+
// Extract options from the request
|
|
440
|
+
const options = {
|
|
441
|
+
preserveIndentation: parsed.data.options?.preserveIndentation ?? true
|
|
442
|
+
};
|
|
443
|
+
const result = await handlePatchFiles(parsed.data.files, parsed.data.dryRun, options, this.allowedDirectories);
|
|
444
|
+
return {
|
|
445
|
+
content: [{ type: "text", text: result }],
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
case "create_directories": {
|
|
449
|
+
const parsed = CreateDirectoriesArgsSchema.safeParse(args);
|
|
450
|
+
if (!parsed.success) {
|
|
451
|
+
throw new Error(`Invalid arguments for create_directories: ${parsed.error}`);
|
|
452
|
+
}
|
|
453
|
+
const result = await handleCreateDirectories(parsed.data.paths, this.allowedDirectories);
|
|
454
|
+
return {
|
|
455
|
+
content: [{ type: "text", text: result }],
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
case "list_directory": {
|
|
459
|
+
const parsed = ListDirectoryArgsSchema.safeParse(args);
|
|
460
|
+
if (!parsed.success) {
|
|
461
|
+
throw new Error(`Invalid arguments for list_directory: ${parsed.error}`);
|
|
462
|
+
}
|
|
463
|
+
const result = await handleListDirectory(parsed.data.path, this.allowedDirectories, parsed.data.type, parsed.data.ignore, parsed.data.offset, parsed.data.limit, parsed.data.includeSizes);
|
|
464
|
+
return {
|
|
465
|
+
content: [{ type: "text", text: result }],
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
case "directory_tree": {
|
|
469
|
+
const parsed = DirectoryTreeArgsSchema.safeParse(args);
|
|
470
|
+
if (!parsed.success) {
|
|
471
|
+
throw new Error(`Invalid arguments for directory_tree: ${parsed.error}`);
|
|
472
|
+
}
|
|
473
|
+
const result = await handleDirectoryTree(parsed.data.path, parsed.data.excludePatterns, this.allowedDirectories);
|
|
474
|
+
return {
|
|
475
|
+
content: [{
|
|
476
|
+
type: "text",
|
|
477
|
+
text: result
|
|
478
|
+
}],
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
case "transfer_files": {
|
|
482
|
+
const parsed = MoveFilesArgsSchema.safeParse(args);
|
|
483
|
+
if (!parsed.success) {
|
|
484
|
+
throw new Error(`Invalid arguments for transfer_files: ${parsed.error}`);
|
|
485
|
+
}
|
|
486
|
+
const result = await handleMoveFiles(parsed.data.items, parsed.data.overwrite, this.allowedDirectories);
|
|
487
|
+
return {
|
|
488
|
+
content: [{ type: "text", text: result }],
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
case "search_files": {
|
|
492
|
+
const parsed = SearchFilesArgsSchema.safeParse(args);
|
|
493
|
+
if (!parsed.success) {
|
|
494
|
+
throw new Error(`Invalid arguments for search_files: ${parsed.error}`);
|
|
495
|
+
}
|
|
496
|
+
const result = await handleSearchFiles(parsed.data.path, parsed.data.pattern, parsed.data.excludePatterns, parsed.data.maxResults, parsed.data.offset, this.allowedDirectories);
|
|
497
|
+
return {
|
|
498
|
+
content: [{ type: "text", text: result }],
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
case "search_regex": {
|
|
502
|
+
const parsed = SearchRegexArgsSchema.safeParse(args);
|
|
503
|
+
if (!parsed.success) {
|
|
504
|
+
throw new Error(`Invalid arguments for search_regex: ${parsed.error}`);
|
|
505
|
+
}
|
|
506
|
+
const result = await handleSearchRegex(parsed.data.path, parsed.data.pattern, parsed.data.filePatterns, parsed.data.excludePatterns, parsed.data.maxResults, parsed.data.caseSensitive, parsed.data.offset, this.allowedDirectories);
|
|
507
|
+
return {
|
|
508
|
+
content: [{ type: "text", text: result }],
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
case "search_glob": {
|
|
512
|
+
const parsed = SearchGlobArgsSchema.safeParse(args);
|
|
513
|
+
if (!parsed.success) {
|
|
514
|
+
throw new Error(`Invalid arguments for search_glob: ${parsed.error}`);
|
|
515
|
+
}
|
|
516
|
+
const result = await handleSearchGlob(parsed.data.path, parsed.data.pattern, parsed.data.excludePatterns, parsed.data.maxResults, parsed.data.offset, this.allowedDirectories);
|
|
517
|
+
return {
|
|
518
|
+
content: [{ type: "text", text: result }],
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
case "count_lines": {
|
|
522
|
+
const parsed = CountLinesArgsSchema.safeParse(args);
|
|
523
|
+
if (!parsed.success) {
|
|
524
|
+
throw new Error(`Invalid arguments for count_lines: ${parsed.error}`);
|
|
525
|
+
}
|
|
526
|
+
const result = await handleCountLines(parsed.data.path, parsed.data.recursive, parsed.data.pattern, parsed.data.filePattern, parsed.data.excludePatterns, parsed.data.ignoreEmptyLines, this.allowedDirectories);
|
|
527
|
+
return {
|
|
528
|
+
content: [{ type: "text", text: result }],
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
case "checksum_files": {
|
|
532
|
+
const parsed = ChecksumFilesArgsSchema.safeParse(args);
|
|
533
|
+
if (!parsed.success) {
|
|
534
|
+
throw new Error(`Invalid arguments for checksum_files: ${parsed.error}`);
|
|
535
|
+
}
|
|
536
|
+
const result = await handleChecksumFiles(parsed.data.paths, parsed.data.algorithm, this.allowedDirectories);
|
|
537
|
+
return {
|
|
538
|
+
content: [{ type: "text", text: result }],
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
case "checksum_files_verif": {
|
|
542
|
+
const parsed = ChecksumFilesVerifArgsSchema.safeParse(args);
|
|
543
|
+
if (!parsed.success) {
|
|
544
|
+
throw new Error(`Invalid arguments for checksum_files_verif: ${parsed.error}`);
|
|
545
|
+
}
|
|
546
|
+
const result = await handleChecksumFilesVerif(parsed.data.files, parsed.data.algorithm, this.allowedDirectories);
|
|
547
|
+
return {
|
|
548
|
+
content: [{ type: "text", text: result }],
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
case "get_file_info": {
|
|
552
|
+
const parsed = GetFileInfoArgsSchema.safeParse(args);
|
|
553
|
+
if (!parsed.success) {
|
|
554
|
+
throw new Error(`Invalid arguments for get_file_info: ${parsed.error}`);
|
|
555
|
+
}
|
|
556
|
+
const result = await handleGetFileInfo(parsed.data.path, this.allowedDirectories);
|
|
557
|
+
return {
|
|
558
|
+
content: [{ type: "text", text: result }],
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
case "list_allowed_directories": {
|
|
562
|
+
return {
|
|
563
|
+
content: [{
|
|
564
|
+
type: "text",
|
|
565
|
+
text: `Allowed directories:\n${this.allowedDirectories.join('\n')}`
|
|
566
|
+
}],
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
case "edit_files": {
|
|
570
|
+
const parsed = EditFilesArgsSchema.safeParse(args);
|
|
571
|
+
if (!parsed.success) {
|
|
572
|
+
throw new Error(`Invalid arguments for edit_files: ${parsed.error}`);
|
|
573
|
+
}
|
|
574
|
+
const result = await handleEditFiles(parsed.data.files, this.allowedDirectories);
|
|
575
|
+
return {
|
|
576
|
+
content: [{ type: "text", text: result }],
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
case "fuzzy_find_files": {
|
|
580
|
+
const parsed = FuzzyFindFilesArgsSchema.safeParse(args);
|
|
581
|
+
if (!parsed.success) {
|
|
582
|
+
throw new Error(`Invalid arguments for fuzzy_find_files: ${parsed.error}`);
|
|
583
|
+
}
|
|
584
|
+
const result = await handleFuzzyFindFiles(parsed.data.query, parsed.data.path, parsed.data.recursive, parsed.data.maxResults, parsed.data.scanLimit, this.allowedDirectories);
|
|
585
|
+
return {
|
|
586
|
+
content: [{ type: "text", text: result }],
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
case "delete_files_by_pattern": {
|
|
590
|
+
const parsed = DeleteFilesByPatternArgsSchema.safeParse(args);
|
|
591
|
+
if (!parsed.success) {
|
|
592
|
+
throw new Error(`Invalid arguments for delete_files_by_pattern: ${parsed.error}`);
|
|
593
|
+
}
|
|
594
|
+
const result = await handleDeleteFilesByPattern(parsed.data.directory, parsed.data.pattern, parsed.data.includeDirectories, this.allowedDirectories);
|
|
595
|
+
return {
|
|
596
|
+
content: [{ type: "text", text: result }],
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
case "shell_run": {
|
|
600
|
+
const parsed = ShellRunArgsSchema.safeParse(args);
|
|
601
|
+
if (!parsed.success) {
|
|
602
|
+
throw new Error(`Invalid arguments for shell_run: ${parsed.error}`);
|
|
603
|
+
}
|
|
604
|
+
const result = await handleShellRun(parsed.data.command, parsed.data.input, parsed.data.timeout_seconds, parsed.data.cwd);
|
|
605
|
+
return {
|
|
606
|
+
content: [{ type: "text", text: result }],
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
case "shell_test": {
|
|
610
|
+
const parsed = ShellTestArgsSchema.safeParse(args);
|
|
611
|
+
if (!parsed.success) {
|
|
612
|
+
throw new Error(`Invalid arguments for shell_test: ${parsed.error}`);
|
|
613
|
+
}
|
|
614
|
+
const result = await handleShellTest(parsed.data.command, parsed.data.cwd);
|
|
615
|
+
return {
|
|
616
|
+
content: [{ type: "text", text: result }],
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
case "shell_start": {
|
|
620
|
+
const parsed = ShellStartArgsSchema.safeParse(args);
|
|
621
|
+
if (!parsed.success) {
|
|
622
|
+
throw new Error(`Invalid arguments for shell_start: ${parsed.error}`);
|
|
623
|
+
}
|
|
624
|
+
const result = await handleShellStart(parsed.data.command, parsed.data.name, parsed.data.timeout_hours, parsed.data.cwd);
|
|
625
|
+
return {
|
|
626
|
+
content: [{ type: "text", text: result }],
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
case "shell_check": {
|
|
630
|
+
const parsed = ShellCheckArgsSchema.safeParse(args);
|
|
631
|
+
if (!parsed.success) {
|
|
632
|
+
throw new Error(`Invalid arguments for shell_check: ${parsed.error}`);
|
|
633
|
+
}
|
|
634
|
+
const result = await handleShellCheck(parsed.data.id);
|
|
635
|
+
return {
|
|
636
|
+
content: [{ type: "text", text: result }],
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
case "shell_cancel": {
|
|
640
|
+
const parsed = ShellCancelArgsSchema.safeParse(args);
|
|
641
|
+
if (!parsed.success) {
|
|
642
|
+
throw new Error(`Invalid arguments for shell_cancel: ${parsed.error}`);
|
|
643
|
+
}
|
|
644
|
+
const result = await handleShellCancel(parsed.data.id);
|
|
645
|
+
return {
|
|
646
|
+
content: [{ type: "text", text: result }],
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
case "shell_terminal": {
|
|
650
|
+
const parsed = ShellTerminalArgsSchema.safeParse(args);
|
|
651
|
+
if (!parsed.success) {
|
|
652
|
+
throw new Error(`Invalid arguments for shell_terminal: ${parsed.error}`);
|
|
653
|
+
}
|
|
654
|
+
const result = await handleShellTerminal(parsed.data.command, parsed.data.cwd);
|
|
655
|
+
return {
|
|
656
|
+
content: [{ type: "text", text: result }],
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
case "shell_python": {
|
|
660
|
+
const parsed = ShellPythonArgsSchema.safeParse(args);
|
|
661
|
+
if (!parsed.success) {
|
|
662
|
+
throw new Error(`Invalid arguments for shell_python: ${parsed.error}`);
|
|
663
|
+
}
|
|
664
|
+
const result = await handleShellPython(parsed.data.python, parsed.data.timeout_seconds, parsed.data.cwd);
|
|
665
|
+
return {
|
|
666
|
+
content: [{ type: "text", text: result }],
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
case "shell_cwd": {
|
|
670
|
+
const parsed = ShellCwdArgsSchema.safeParse(args);
|
|
671
|
+
if (!parsed.success) {
|
|
672
|
+
throw new Error(`Invalid arguments for shell_cwd: ${parsed.error}`);
|
|
673
|
+
}
|
|
674
|
+
const result = await handleShellCwd(parsed.data.path);
|
|
675
|
+
return {
|
|
676
|
+
content: [{ type: "text", text: result }],
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
default:
|
|
680
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
685
|
+
return {
|
|
686
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
687
|
+
isError: true,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
async connect() {
|
|
693
|
+
const transport = new StdioServerTransport();
|
|
694
|
+
await this.server.connect(transport);
|
|
695
|
+
console.error("Secure MCP Filesystem Server running on stdio");
|
|
696
|
+
console.error("Allowed directories:", this.allowedDirectories);
|
|
697
|
+
}
|
|
698
|
+
}
|