@j0hanz/filesystem-mcp 1.5.0 → 1.5.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/dist/lib/constants.d.ts +10 -1
- package/dist/lib/constants.js +11 -1
- package/dist/resources/tool-info.js +2 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +18 -16
- package/dist/tools/calculate-hash.js +1 -1
- package/dist/tools/create-directory.js +2 -2
- package/dist/tools/edit-file.js +10 -3
- package/dist/tools/list-directory.js +1 -1
- package/dist/tools/replace-in-files.js +1 -1
- package/dist/tools/search-content.js +1 -1
- package/dist/tools/shared.d.ts +2 -0
- package/dist/tools/shared.js +2 -0
- package/package.json +3 -3
package/dist/lib/constants.d.ts
CHANGED
|
@@ -11,8 +11,17 @@ export declare const DEFAULT_SEARCH_TIMEOUT_MS: number;
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const SEARCH_WORKERS: number;
|
|
13
13
|
export declare const DEFAULT_MAX_DEPTH = 10;
|
|
14
|
-
export declare const DEFAULT_LIST_MAX_ENTRIES =
|
|
14
|
+
export declare const DEFAULT_LIST_MAX_ENTRIES = 20000;
|
|
15
15
|
export declare const DEFAULT_SEARCH_MAX_FILES = 20000;
|
|
16
|
+
export declare const MAX_TREE_DEPTH = 50;
|
|
17
|
+
export declare const DEFAULT_TREE_DEPTH = 5;
|
|
18
|
+
export declare const MAX_TREE_ENTRIES = 20000;
|
|
19
|
+
export declare const DEFAULT_TREE_ENTRIES = 1000;
|
|
20
|
+
export declare const MAX_LIST_ENTRIES = 20000;
|
|
21
|
+
export declare const MAX_SEARCH_RESULTS = 10000;
|
|
22
|
+
export declare const DEFAULT_SEARCH_RESULTS = 100;
|
|
23
|
+
export declare const MAX_SEARCH_DEPTH = 100;
|
|
24
|
+
export declare const DEFAULT_SEARCH_CONTENT_RESULTS = 500;
|
|
16
25
|
export declare const MAX_LINE_CONTENT_LENGTH = 200;
|
|
17
26
|
export declare const BINARY_CHECK_BUFFER_SIZE = 512;
|
|
18
27
|
export declare const SENSITIVE_FILE_DENYLIST: string[];
|
package/dist/lib/constants.js
CHANGED
|
@@ -109,8 +109,18 @@ const ENV_ALLOWLIST = parseEnvList('FS_CONTEXT_ALLOWLIST');
|
|
|
109
109
|
export const SEARCH_WORKERS = parseEnvInt('FS_CONTEXT_SEARCH_WORKERS', getDefaultSearchWorkers(), 1, 16);
|
|
110
110
|
// Hardcoded defaults
|
|
111
111
|
export const DEFAULT_MAX_DEPTH = 10;
|
|
112
|
-
export const DEFAULT_LIST_MAX_ENTRIES =
|
|
112
|
+
export const DEFAULT_LIST_MAX_ENTRIES = 20000;
|
|
113
113
|
export const DEFAULT_SEARCH_MAX_FILES = 20000;
|
|
114
|
+
// Schema limits and defaults
|
|
115
|
+
export const MAX_TREE_DEPTH = 50;
|
|
116
|
+
export const DEFAULT_TREE_DEPTH = 5;
|
|
117
|
+
export const MAX_TREE_ENTRIES = 20000;
|
|
118
|
+
export const DEFAULT_TREE_ENTRIES = 1000;
|
|
119
|
+
export const MAX_LIST_ENTRIES = 20000;
|
|
120
|
+
export const MAX_SEARCH_RESULTS = 10000;
|
|
121
|
+
export const DEFAULT_SEARCH_RESULTS = 100;
|
|
122
|
+
export const MAX_SEARCH_DEPTH = 100;
|
|
123
|
+
export const DEFAULT_SEARCH_CONTENT_RESULTS = 500;
|
|
114
124
|
// Non-configurable constants
|
|
115
125
|
export const MAX_LINE_CONTENT_LENGTH = 200;
|
|
116
126
|
export const BINARY_CHECK_BUFFER_SIZE = 512;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_SEARCH_CONTENT_RESULTS, MAX_SEARCH_RESULTS, MAX_TEXT_FILE_SIZE, } from '../lib/constants.js';
|
|
1
2
|
import { ALL_TOOLS } from '../tools.js';
|
|
2
3
|
function toEntry(contract) {
|
|
3
4
|
const annotations = [];
|
|
@@ -38,7 +39,7 @@ export function getSharedConstraints() {
|
|
|
38
39
|
return [
|
|
39
40
|
'Allowed roots only (negotiated via CLI).',
|
|
40
41
|
'Sensitive files denylisted by default.',
|
|
41
|
-
|
|
42
|
+
`Max file size (${Math.floor(MAX_TEXT_FILE_SIZE / 1024 / 1024)}MB) & search results (${MAX_SEARCH_RESULTS} files, ${DEFAULT_SEARCH_CONTENT_RESULTS} lines) enforced.`,
|
|
42
43
|
'Externalized results are ephemeral (in-memory).',
|
|
43
44
|
];
|
|
44
45
|
}
|
package/dist/schemas.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare const ListDirectoryInputSchema: z.ZodObject<{
|
|
|
38
38
|
includeHidden: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
39
39
|
includeIgnored: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
40
40
|
maxDepth: z.ZodOptional<z.ZodNumber>;
|
|
41
|
-
maxEntries: z.ZodOptional<z.ZodNumber
|
|
41
|
+
maxEntries: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
42
42
|
sortBy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
43
43
|
name: "name";
|
|
44
44
|
size: "size";
|
package/dist/schemas.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { DEFAULT_LIST_MAX_ENTRIES, DEFAULT_SEARCH_CONTENT_RESULTS, DEFAULT_SEARCH_RESULTS, DEFAULT_TREE_DEPTH, DEFAULT_TREE_ENTRIES, MAX_LIST_ENTRIES, MAX_SEARCH_DEPTH, MAX_SEARCH_RESULTS, MAX_TREE_DEPTH, MAX_TREE_ENTRIES, } from './lib/constants.js';
|
|
2
3
|
import { ErrorCode } from './lib/errors.js';
|
|
3
4
|
function isSafeGlobPattern(value) {
|
|
4
5
|
if (value.length === 0)
|
|
@@ -111,16 +112,17 @@ export const ListDirectoryInputSchema = z.strictObject({
|
|
|
111
112
|
.number()
|
|
112
113
|
.int({ error: 'Must be integer' })
|
|
113
114
|
.min(1, 'Min: 1')
|
|
114
|
-
.max(
|
|
115
|
+
.max(MAX_TREE_DEPTH, `Max: ${MAX_TREE_DEPTH}`)
|
|
115
116
|
.optional()
|
|
116
117
|
.describe('Max recursion depth when pattern is provided'),
|
|
117
118
|
maxEntries: z
|
|
118
119
|
.number()
|
|
119
120
|
.int({ error: 'Must be integer' })
|
|
120
121
|
.min(1, 'Min: 1')
|
|
121
|
-
.max(
|
|
122
|
+
.max(MAX_LIST_ENTRIES, `Max: ${MAX_LIST_ENTRIES}`)
|
|
122
123
|
.optional()
|
|
123
|
-
.
|
|
124
|
+
.default(DEFAULT_LIST_MAX_ENTRIES)
|
|
125
|
+
.describe(`Maximum entries to return before truncation. Default: ${DEFAULT_LIST_MAX_ENTRIES}`),
|
|
124
126
|
sortBy: ListDirectorySortSchema.optional()
|
|
125
127
|
.default('name')
|
|
126
128
|
.describe('Sort field (name, size, modified, type)'),
|
|
@@ -157,10 +159,10 @@ export const SearchFilesInputSchema = z.strictObject({
|
|
|
157
159
|
.number()
|
|
158
160
|
.int({ error: 'Must be integer' })
|
|
159
161
|
.min(1, 'Min: 1')
|
|
160
|
-
.max(
|
|
162
|
+
.max(MAX_SEARCH_RESULTS, `Max: ${MAX_SEARCH_RESULTS}`)
|
|
161
163
|
.optional()
|
|
162
|
-
.default(
|
|
163
|
-
.describe(
|
|
164
|
+
.default(DEFAULT_SEARCH_RESULTS)
|
|
165
|
+
.describe(`Max results (1-${MAX_SEARCH_RESULTS}). Default: ${DEFAULT_SEARCH_RESULTS}`),
|
|
164
166
|
includeIgnored: z
|
|
165
167
|
.boolean()
|
|
166
168
|
.optional()
|
|
@@ -178,7 +180,7 @@ export const SearchFilesInputSchema = z.strictObject({
|
|
|
178
180
|
.number()
|
|
179
181
|
.int({ error: 'Must be integer' })
|
|
180
182
|
.min(0, 'Min: 0')
|
|
181
|
-
.max(
|
|
183
|
+
.max(MAX_SEARCH_DEPTH, `Max: ${MAX_SEARCH_DEPTH}`)
|
|
182
184
|
.optional()
|
|
183
185
|
.describe('Maximum directory depth to scan'),
|
|
184
186
|
cursor: z
|
|
@@ -192,18 +194,18 @@ export const TreeInputSchema = z.strictObject({
|
|
|
192
194
|
.number()
|
|
193
195
|
.int({ error: 'Must be integer' })
|
|
194
196
|
.min(0, 'Min: 0')
|
|
195
|
-
.max(
|
|
197
|
+
.max(MAX_TREE_DEPTH, `Max: ${MAX_TREE_DEPTH}`)
|
|
196
198
|
.optional()
|
|
197
|
-
.default(
|
|
198
|
-
.describe(
|
|
199
|
+
.default(DEFAULT_TREE_DEPTH)
|
|
200
|
+
.describe(`Depth (0=root node only, no children). Default: ${DEFAULT_TREE_DEPTH}`),
|
|
199
201
|
maxEntries: z
|
|
200
202
|
.number()
|
|
201
203
|
.int({ error: 'Must be integer' })
|
|
202
204
|
.min(1, 'Min: 1')
|
|
203
|
-
.max(
|
|
205
|
+
.max(MAX_TREE_ENTRIES, `Max: ${MAX_TREE_ENTRIES}`)
|
|
204
206
|
.optional()
|
|
205
|
-
.default(
|
|
206
|
-
.describe(
|
|
207
|
+
.default(DEFAULT_TREE_ENTRIES)
|
|
208
|
+
.describe(`Max entries. Default: ${DEFAULT_TREE_ENTRIES}`),
|
|
207
209
|
includeHidden: z
|
|
208
210
|
.boolean()
|
|
209
211
|
.optional()
|
|
@@ -249,10 +251,10 @@ export const SearchContentInputSchema = z.strictObject({
|
|
|
249
251
|
.number()
|
|
250
252
|
.int({ error: 'Must be integer' })
|
|
251
253
|
.min(0, 'Min: 0')
|
|
252
|
-
.max(
|
|
254
|
+
.max(MAX_SEARCH_RESULTS, `Max: ${MAX_SEARCH_RESULTS}`)
|
|
253
255
|
.optional()
|
|
254
|
-
.default(
|
|
255
|
-
.describe(
|
|
256
|
+
.default(DEFAULT_SEARCH_CONTENT_RESULTS)
|
|
257
|
+
.describe(`Maximum match rows to return. Default: ${DEFAULT_SEARCH_CONTENT_RESULTS}`),
|
|
256
258
|
filePattern: z
|
|
257
259
|
.string()
|
|
258
260
|
.min(1, 'Pattern required')
|
|
@@ -171,7 +171,7 @@ export function registerCalculateHashTool(server, options = {}) {
|
|
|
171
171
|
baseReporter({
|
|
172
172
|
current,
|
|
173
173
|
...(total !== undefined ? { total } : {}),
|
|
174
|
-
message: `🕮 calculate_hash: ${baseName}
|
|
174
|
+
message: `🕮 calculate_hash: ${baseName} [${current} ${fileWord} hashed]`,
|
|
175
175
|
});
|
|
176
176
|
};
|
|
177
177
|
try {
|
|
@@ -34,11 +34,11 @@ export function registerCreateDirectoryTool(server, options = {}) {
|
|
|
34
34
|
const wrappedHandler = wrapToolHandler(handler, {
|
|
35
35
|
guard: options.isInitialized,
|
|
36
36
|
progressMessage: (args) => {
|
|
37
|
-
const name = path.basename(args.path) ||
|
|
37
|
+
const name = path.basename(args.path) || '.';
|
|
38
38
|
return `🛠 mkdir: ${name}`;
|
|
39
39
|
},
|
|
40
40
|
completionMessage: (args, result) => {
|
|
41
|
-
const name = path.basename(args.path) ||
|
|
41
|
+
const name = path.basename(args.path) || '.';
|
|
42
42
|
if (result.isError)
|
|
43
43
|
return `🛠 mkdir: ${name} • failed`;
|
|
44
44
|
return `🛠 mkdir: ${name} • created`;
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -96,7 +96,8 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
96
96
|
guard: options.isInitialized,
|
|
97
97
|
progressMessage: (args) => {
|
|
98
98
|
const name = path.basename(args.path);
|
|
99
|
-
|
|
99
|
+
const dryTag = args.dryRun ? ' [dry run]' : '';
|
|
100
|
+
return `🛠 edit: ${name} [${args.edits.length} edits]${dryTag}`;
|
|
100
101
|
},
|
|
101
102
|
completionMessage: (args, result) => {
|
|
102
103
|
const name = path.basename(args.path);
|
|
@@ -105,10 +106,16 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
105
106
|
const sc = result.structuredContent;
|
|
106
107
|
if (!sc.ok)
|
|
107
108
|
return `🛠 edit: ${name} • failed`;
|
|
109
|
+
const applied = sc.appliedEdits ?? 0;
|
|
110
|
+
const unmatched = sc.unmatchedEdits?.length ?? 0;
|
|
111
|
+
const dryPrefix = args.dryRun ? 'dry run — ' : '';
|
|
112
|
+
if (unmatched > 0) {
|
|
113
|
+
return `🛠 edit: ${name} • ${dryPrefix}${applied} applied, ${unmatched} unmatched`;
|
|
114
|
+
}
|
|
108
115
|
if (sc.lineRange) {
|
|
109
|
-
return `🛠 edit: ${name} •
|
|
116
|
+
return `🛠 edit: ${name} • ${dryPrefix}lines ${sc.lineRange[0]}–${sc.lineRange[1]}`;
|
|
110
117
|
}
|
|
111
|
-
return `🛠 edit: ${name} •
|
|
118
|
+
return `🛠 edit: ${name} • ${dryPrefix}${applied} applied`;
|
|
112
119
|
},
|
|
113
120
|
});
|
|
114
121
|
const validatedHandler = withValidatedArgs(EditFileInputSchema, wrappedHandler);
|
|
@@ -98,7 +98,7 @@ function decodeCursor(cursor) {
|
|
|
98
98
|
async function handleListDirectory(args, signal) {
|
|
99
99
|
const dirPath = resolvePathOrRoot(args.path);
|
|
100
100
|
const cursorOffset = args.cursor !== undefined ? decodeCursor(args.cursor) : 0;
|
|
101
|
-
const pageSize = args.maxEntries
|
|
101
|
+
const pageSize = args.maxEntries;
|
|
102
102
|
const options = {
|
|
103
103
|
includeHidden: args.includeHidden,
|
|
104
104
|
excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,
|
|
@@ -265,7 +265,7 @@ export function registerSearchAndReplaceTool(server, options = {}) {
|
|
|
265
265
|
baseReporter({
|
|
266
266
|
current,
|
|
267
267
|
...(total !== undefined ? { total } : {}),
|
|
268
|
-
message: `🛠 search_and_replace:
|
|
268
|
+
message: `🛠 search_and_replace: ${args.searchPattern} [${current} files processed]`,
|
|
269
269
|
});
|
|
270
270
|
};
|
|
271
271
|
try {
|
|
@@ -220,7 +220,7 @@ export function registerSearchContentTool(server, options = {}) {
|
|
|
220
220
|
baseReporter({
|
|
221
221
|
current,
|
|
222
222
|
...(total !== undefined ? { total } : {}),
|
|
223
|
-
message: `🔎︎ grep: ${pattern}
|
|
223
|
+
message: `🔎︎ grep: ${pattern} [${current} ${fileWord} scanned]`,
|
|
224
224
|
});
|
|
225
225
|
};
|
|
226
226
|
try {
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { type ToolContract } from './contract.js';
|
|
|
8
8
|
export declare const READ_ONLY_TOOL_ANNOTATIONS: {
|
|
9
9
|
readonly readOnlyHint: true;
|
|
10
10
|
readonly idempotentHint: true;
|
|
11
|
+
readonly destructiveHint: false;
|
|
11
12
|
readonly openWorldHint: false;
|
|
12
13
|
};
|
|
13
14
|
export declare const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS: {
|
|
@@ -18,6 +19,7 @@ export declare const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS: {
|
|
|
18
19
|
export declare const IDEMPOTENT_WRITE_TOOL_ANNOTATIONS: {
|
|
19
20
|
readonly readOnlyHint: false;
|
|
20
21
|
readonly idempotentHint: true;
|
|
22
|
+
readonly destructiveHint: false;
|
|
21
23
|
readonly openWorldHint: false;
|
|
22
24
|
};
|
|
23
25
|
export declare function shouldStripStructuredOutput(): boolean;
|
package/dist/tools/shared.js
CHANGED
|
@@ -18,6 +18,7 @@ function publishContextDiagnostics(event) {
|
|
|
18
18
|
export const READ_ONLY_TOOL_ANNOTATIONS = {
|
|
19
19
|
readOnlyHint: true,
|
|
20
20
|
idempotentHint: true,
|
|
21
|
+
destructiveHint: false,
|
|
21
22
|
openWorldHint: false,
|
|
22
23
|
};
|
|
23
24
|
export const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS = {
|
|
@@ -28,6 +29,7 @@ export const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS = {
|
|
|
28
29
|
export const IDEMPOTENT_WRITE_TOOL_ANNOTATIONS = {
|
|
29
30
|
readOnlyHint: false,
|
|
30
31
|
idempotentHint: true,
|
|
32
|
+
destructiveHint: false,
|
|
31
33
|
openWorldHint: false,
|
|
32
34
|
};
|
|
33
35
|
export function shouldStripStructuredOutput() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j0hanz/filesystem-mcp",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"mcpName": "io.github.j0hanz/filesystem-mcp",
|
|
5
5
|
"description": "MCP Server that enables LLMs to interact with the local filesystem.",
|
|
6
6
|
"type": "module",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@eslint/js": "^10.0.1",
|
|
76
|
-
"eslint": "^10.0.
|
|
76
|
+
"eslint": "^10.0.1",
|
|
77
77
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
78
78
|
"@types/node": "^24",
|
|
79
79
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"eslint-plugin-depend": "^1.4.0",
|
|
82
82
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
83
83
|
"jscpd": "^4.0.8",
|
|
84
|
-
"knip": "^5.
|
|
84
|
+
"knip": "^5.85.0",
|
|
85
85
|
"prettier": "^3.8.1",
|
|
86
86
|
"tsx": "^4.21.0",
|
|
87
87
|
"typescript": "^5.9.3",
|