@j0hanz/filesystem-mcp 1.8.0 → 1.9.0
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/README.md +635 -377
- package/dist/cli.js +2 -2
- package/dist/completions.js +2 -3
- package/dist/index.js +2 -2
- package/dist/lib/file-operations/{common.d.ts → core.d.ts} +6 -0
- package/dist/lib/file-operations/{common.js → core.js} +45 -0
- package/dist/lib/file-operations/metadata.d.ts +73 -0
- package/dist/lib/file-operations/metadata.js +889 -0
- package/dist/lib/file-operations/{search-content.d.ts → search.d.ts} +35 -2
- package/dist/lib/file-operations/{search-content.js → search.js} +413 -15
- package/dist/lib/file-operations/{glob-engine.d.ts → traversal.d.ts} +18 -1
- package/dist/lib/file-operations/{glob-engine.js → traversal.js} +25 -2
- package/dist/lib/fs-helpers.js +1 -2
- package/dist/lib/observability.js +1 -1
- package/dist/lib/{path-validation.d.ts → paths.d.ts} +3 -0
- package/dist/lib/{path-validation.js → paths.js} +105 -1
- package/dist/lib/utils.d.ts +15 -0
- package/dist/lib/utils.js +33 -0
- package/dist/resources/generated-instructions.js +6 -6
- package/dist/resources/tool-catalog.js +9 -9
- package/dist/resources/tool-info.js +3 -3
- package/dist/resources/workflows.js +10 -23
- package/dist/schemas.js +15 -15
- package/dist/server/bootstrap.d.ts +19 -1
- package/dist/server/bootstrap.js +103 -9
- package/dist/server/roots-manager.d.ts +2 -2
- package/dist/server/roots-manager.js +3 -3
- package/dist/tools/apply-patch.js +4 -3
- package/dist/tools/calculate-hash.js +4 -4
- package/dist/tools/create-directory.js +1 -1
- package/dist/tools/delete-file.js +3 -3
- package/dist/tools/diff-files.js +2 -2
- package/dist/tools/edit-file.js +14 -13
- package/dist/tools/list-directory.js +5 -7
- package/dist/tools/move-file.js +2 -1
- package/dist/tools/read-multiple.js +3 -4
- package/dist/tools/read.js +3 -3
- package/dist/tools/replace-in-files.js +8 -10
- package/dist/tools/roots.js +4 -6
- package/dist/tools/search-content.js +7 -9
- package/dist/tools/search-files.js +4 -6
- package/dist/tools/shared.d.ts +2 -1
- package/dist/tools/shared.js +2 -1
- package/dist/tools/stat-many.js +4 -3
- package/dist/tools/stat.js +4 -3
- package/dist/tools/task-support.js +1 -1
- package/dist/tools/tree.js +3 -4
- package/dist/tools/write-file.js +1 -1
- package/package.json +4 -4
- package/dist/lib/file-operations/file-info.d.ts +0 -10
- package/dist/lib/file-operations/file-info.js +0 -143
- package/dist/lib/file-operations/gitignore.d.ts +0 -6
- package/dist/lib/file-operations/gitignore.js +0 -45
- package/dist/lib/file-operations/glob-helpers.d.ts +0 -18
- package/dist/lib/file-operations/glob-helpers.js +0 -23
- package/dist/lib/file-operations/list-directory.d.ts +0 -14
- package/dist/lib/file-operations/list-directory.js +0 -252
- package/dist/lib/file-operations/read-multiple-files.d.ts +0 -25
- package/dist/lib/file-operations/read-multiple-files.js +0 -252
- package/dist/lib/file-operations/search-files.d.ts +0 -27
- package/dist/lib/file-operations/search-files.js +0 -216
- package/dist/lib/file-operations/search-matcher.d.ts +0 -10
- package/dist/lib/file-operations/search-matcher.js +0 -72
- package/dist/lib/file-operations/search-worker.d.ts +0 -2
- package/dist/lib/file-operations/search-worker.js +0 -131
- package/dist/lib/file-operations/tree.d.ts +0 -28
- package/dist/lib/file-operations/tree.js +0 -265
- package/dist/lib/option-utils.d.ts +0 -3
- package/dist/lib/option-utils.js +0 -15
- package/dist/lib/path-format.d.ts +0 -1
- package/dist/lib/path-format.js +0 -7
- package/dist/lib/path-policy.d.ts +0 -2
- package/dist/lib/path-policy.js +0 -100
- package/dist/lib/progress-reporting.d.ts +0 -11
- package/dist/lib/progress-reporting.js +0 -13
- package/dist/lib/type-guards.d.ts +0 -1
- package/dist/lib/type-guards.js +0 -3
- package/dist/server/capabilities.d.ts +0 -10
- package/dist/server/capabilities.js +0 -48
- package/dist/server/logging.d.ts +0 -7
- package/dist/server/logging.js +0 -41
- package/dist/server/types.d.ts +0 -4
- package/dist/server/types.js +0 -1
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
|
-
import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_SEARCH_TIMEOUT_MS, } from '../constants.js';
|
|
3
|
-
import { withTimedAbortSignal } from '../fs-helpers.js';
|
|
4
|
-
import { toPosixPath } from '../path-format.js';
|
|
5
|
-
import { isSensitivePath } from '../path-policy.js';
|
|
6
|
-
import { isPathWithinDirectories, normalizePath, validateExistingDirectory, validateExistingPathDetailed, } from '../path-validation.js';
|
|
7
|
-
import { isEntryAccessibleByType, resolveEntryType, resolveStopReason, } from './common.js';
|
|
8
|
-
import { isIgnoredByGitignore, loadRootGitignore } from './gitignore.js';
|
|
9
|
-
import { globEntries } from './glob-engine.js';
|
|
10
|
-
function toSafeNonNegativeInt(value, fallback) {
|
|
11
|
-
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
12
|
-
return fallback;
|
|
13
|
-
const asInt = Math.floor(value);
|
|
14
|
-
return asInt >= 0 ? asInt : fallback;
|
|
15
|
-
}
|
|
16
|
-
function toSafePositiveInt(value, fallback) {
|
|
17
|
-
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
18
|
-
return fallback;
|
|
19
|
-
const asInt = Math.floor(value);
|
|
20
|
-
return asInt > 0 ? asInt : fallback;
|
|
21
|
-
}
|
|
22
|
-
function toSafeBoolean(value, fallback) {
|
|
23
|
-
if (typeof value !== 'boolean')
|
|
24
|
-
return fallback;
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
function normalizeOptions(options) {
|
|
28
|
-
return {
|
|
29
|
-
maxDepth: toSafeNonNegativeInt(options.maxDepth, 5),
|
|
30
|
-
maxEntries: toSafeNonNegativeInt(options.maxEntries, 1000),
|
|
31
|
-
includeHidden: toSafeBoolean(options.includeHidden, false),
|
|
32
|
-
includeIgnored: toSafeBoolean(options.includeIgnored, false),
|
|
33
|
-
timeoutMs: toSafePositiveInt(options.timeoutMs, DEFAULT_SEARCH_TIMEOUT_MS),
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function ensureParentNodes(rootNode, nodeByPath, relativePath) {
|
|
37
|
-
const normalized = toPosixPath(relativePath);
|
|
38
|
-
if (normalized.length === 0 || normalized === '.')
|
|
39
|
-
return rootNode;
|
|
40
|
-
const segments = [];
|
|
41
|
-
for (const segment of normalized.split('/')) {
|
|
42
|
-
if (segment.length > 0) {
|
|
43
|
-
segments.push(segment);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const parentSegmentCount = Math.max(0, segments.length - 1);
|
|
47
|
-
let current = rootNode;
|
|
48
|
-
let currentPath = '';
|
|
49
|
-
for (let index = 0; index < parentSegmentCount; index += 1) {
|
|
50
|
-
const segment = segments[index];
|
|
51
|
-
if (!segment)
|
|
52
|
-
continue;
|
|
53
|
-
currentPath =
|
|
54
|
-
currentPath.length === 0
|
|
55
|
-
? segment
|
|
56
|
-
: path.posix.join(currentPath, segment);
|
|
57
|
-
let child = nodeByPath.get(currentPath);
|
|
58
|
-
if (!child) {
|
|
59
|
-
child = {
|
|
60
|
-
name: segment,
|
|
61
|
-
type: 'directory',
|
|
62
|
-
relativePath: currentPath,
|
|
63
|
-
children: [],
|
|
64
|
-
};
|
|
65
|
-
nodeByPath.set(currentPath, child);
|
|
66
|
-
current.children ??= [];
|
|
67
|
-
current.children.push(child);
|
|
68
|
-
}
|
|
69
|
-
current = child;
|
|
70
|
-
}
|
|
71
|
-
return current;
|
|
72
|
-
}
|
|
73
|
-
function sortTree(node) {
|
|
74
|
-
if (!node.children)
|
|
75
|
-
return;
|
|
76
|
-
node.children.sort(compareTreeEntries);
|
|
77
|
-
for (const child of node.children) {
|
|
78
|
-
sortTree(child);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function compareTreeEntries(a, b) {
|
|
82
|
-
const diff = getTreeTypeRank(a.type) - getTreeTypeRank(b.type);
|
|
83
|
-
if (diff !== 0)
|
|
84
|
-
return diff;
|
|
85
|
-
return a.name.localeCompare(b.name);
|
|
86
|
-
}
|
|
87
|
-
function getTreeTypeRank(type) {
|
|
88
|
-
if (type === 'directory')
|
|
89
|
-
return 0;
|
|
90
|
-
if (type === 'file')
|
|
91
|
-
return 1;
|
|
92
|
-
return 2;
|
|
93
|
-
}
|
|
94
|
-
async function resolveTreeEntry(entry, root, rootDirectories, gitignoreMatcher, signal, accessDeps) {
|
|
95
|
-
const type = resolveEntryType(entry.dirent);
|
|
96
|
-
const isAccessible = await isEntryAccessibleByType(entry.path, type, rootDirectories, signal, accessDeps);
|
|
97
|
-
if (!isAccessible) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
if (gitignoreMatcher &&
|
|
101
|
-
isIgnoredByGitignore(gitignoreMatcher, root, entry.path, {
|
|
102
|
-
isDirectory: type === 'directory',
|
|
103
|
-
})) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
const relative = path.relative(root, entry.path) || path.basename(entry.path);
|
|
107
|
-
const relativePosix = toPosixPath(relative);
|
|
108
|
-
const name = path.basename(entry.path);
|
|
109
|
-
return { type, relativePosix, name };
|
|
110
|
-
}
|
|
111
|
-
function upsertChildNode(parent, nodeByPath, resolved, childPathIndexByParent) {
|
|
112
|
-
const ensureDirectoryShape = (node) => {
|
|
113
|
-
if (node.type === 'directory') {
|
|
114
|
-
node.children ??= [];
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
delete node.children;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
const maybeUpdateType = (existing, nextType) => {
|
|
121
|
-
if (existing.type === nextType)
|
|
122
|
-
return;
|
|
123
|
-
const preservePopulatedDirectory = existing.type === 'directory' &&
|
|
124
|
-
Array.isArray(existing.children) &&
|
|
125
|
-
existing.children.length > 0;
|
|
126
|
-
if (preservePopulatedDirectory)
|
|
127
|
-
return;
|
|
128
|
-
existing.type = nextType;
|
|
129
|
-
ensureDirectoryShape(existing);
|
|
130
|
-
};
|
|
131
|
-
const attachChild = (child) => {
|
|
132
|
-
parent.children ??= [];
|
|
133
|
-
let seen = childPathIndexByParent.get(parent);
|
|
134
|
-
if (!seen) {
|
|
135
|
-
seen = new Set();
|
|
136
|
-
for (const entry of parent.children) {
|
|
137
|
-
seen.add(entry.relativePath);
|
|
138
|
-
}
|
|
139
|
-
childPathIndexByParent.set(parent, seen);
|
|
140
|
-
}
|
|
141
|
-
const key = child.relativePath;
|
|
142
|
-
if (seen.has(key))
|
|
143
|
-
return;
|
|
144
|
-
seen.add(key);
|
|
145
|
-
parent.children.push(child);
|
|
146
|
-
};
|
|
147
|
-
const existing = nodeByPath.get(resolved.relativePosix);
|
|
148
|
-
if (existing) {
|
|
149
|
-
// Avoid duplicate directory nodes when a child file is encountered before the directory entry.
|
|
150
|
-
// Prefer preserving an existing populated directory node over overwriting it.
|
|
151
|
-
maybeUpdateType(existing, resolved.type);
|
|
152
|
-
existing.name = resolved.name;
|
|
153
|
-
existing.relativePath = resolved.relativePosix;
|
|
154
|
-
ensureDirectoryShape(existing);
|
|
155
|
-
attachChild(existing);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const node = {
|
|
159
|
-
name: resolved.name,
|
|
160
|
-
type: resolved.type,
|
|
161
|
-
relativePath: resolved.relativePosix,
|
|
162
|
-
...(resolved.type === 'directory' ? { children: [] } : {}),
|
|
163
|
-
};
|
|
164
|
-
nodeByPath.set(resolved.relativePosix, node);
|
|
165
|
-
attachChild(node);
|
|
166
|
-
}
|
|
167
|
-
export function formatTreeAscii(tree) {
|
|
168
|
-
const lines = [];
|
|
169
|
-
const walk = (node, prefix, isLast, isRoot) => {
|
|
170
|
-
let connector = '';
|
|
171
|
-
let linePrefix = '';
|
|
172
|
-
if (!isRoot) {
|
|
173
|
-
connector = isLast ? '└── ' : '├── ';
|
|
174
|
-
linePrefix = prefix;
|
|
175
|
-
}
|
|
176
|
-
lines.push(`${linePrefix}${connector}${node.name}`);
|
|
177
|
-
if (!node.children || node.children.length === 0)
|
|
178
|
-
return;
|
|
179
|
-
let nextPrefix = '';
|
|
180
|
-
if (!isRoot) {
|
|
181
|
-
const continuation = isLast ? ' ' : '│ ';
|
|
182
|
-
nextPrefix = `${prefix}${continuation}`;
|
|
183
|
-
}
|
|
184
|
-
const count = node.children.length;
|
|
185
|
-
for (let index = 0; index < count; index += 1) {
|
|
186
|
-
const child = node.children[index];
|
|
187
|
-
if (child) {
|
|
188
|
-
walk(child, nextPrefix, index === count - 1, false);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
walk(tree, '', true, true);
|
|
193
|
-
return lines.join('\n');
|
|
194
|
-
}
|
|
195
|
-
export async function treeDirectory(dirPath, options = {}) {
|
|
196
|
-
const normalized = normalizeOptions(options);
|
|
197
|
-
return withTimedAbortSignal(options.signal, normalized.timeoutMs, async (signal) => {
|
|
198
|
-
const root = await validateExistingDirectory(dirPath, signal);
|
|
199
|
-
const rootNormalized = normalizePath(root);
|
|
200
|
-
const rootDirectories = [rootNormalized];
|
|
201
|
-
const accessDeps = {
|
|
202
|
-
normalizePath,
|
|
203
|
-
isPathWithinDirectories,
|
|
204
|
-
isSensitivePath,
|
|
205
|
-
validateSymlinkPath: validateExistingPathDetailed,
|
|
206
|
-
};
|
|
207
|
-
const excludePatterns = normalized.includeIgnored
|
|
208
|
-
? []
|
|
209
|
-
: DEFAULT_EXCLUDE_PATTERNS;
|
|
210
|
-
const gitignoreMatcher = normalized.includeIgnored
|
|
211
|
-
? null
|
|
212
|
-
: await loadRootGitignore(root, signal);
|
|
213
|
-
const rootNode = {
|
|
214
|
-
name: path.basename(root) || root,
|
|
215
|
-
type: 'directory',
|
|
216
|
-
relativePath: '.',
|
|
217
|
-
children: [],
|
|
218
|
-
};
|
|
219
|
-
const nodeByPath = new Map();
|
|
220
|
-
const childPathIndexByParent = new WeakMap();
|
|
221
|
-
let totalEntries = 0;
|
|
222
|
-
let truncated = false;
|
|
223
|
-
const stream = globEntries({
|
|
224
|
-
cwd: root,
|
|
225
|
-
pattern: '**/*',
|
|
226
|
-
excludePatterns,
|
|
227
|
-
includeHidden: normalized.includeHidden,
|
|
228
|
-
baseNameMatch: false,
|
|
229
|
-
caseSensitiveMatch: true,
|
|
230
|
-
maxDepth: normalized.maxDepth,
|
|
231
|
-
followSymbolicLinks: false,
|
|
232
|
-
onlyFiles: false,
|
|
233
|
-
stats: false,
|
|
234
|
-
suppressErrors: true,
|
|
235
|
-
});
|
|
236
|
-
for await (const entry of stream) {
|
|
237
|
-
const stopReason = resolveStopReason({
|
|
238
|
-
signal,
|
|
239
|
-
current: totalEntries,
|
|
240
|
-
max: normalized.maxEntries,
|
|
241
|
-
abortedReason: 'aborted',
|
|
242
|
-
maxReason: 'maxEntries',
|
|
243
|
-
});
|
|
244
|
-
if (stopReason) {
|
|
245
|
-
truncated = true;
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
const resolved = await resolveTreeEntry(entry, root, rootDirectories, gitignoreMatcher, signal, accessDeps);
|
|
249
|
-
if (!resolved) {
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
const parent = ensureParentNodes(rootNode, nodeByPath, resolved.relativePosix);
|
|
253
|
-
upsertChildNode(parent, nodeByPath, resolved, childPathIndexByParent);
|
|
254
|
-
totalEntries += 1;
|
|
255
|
-
options.onProgress?.({ current: totalEntries });
|
|
256
|
-
}
|
|
257
|
-
sortTree(rootNode);
|
|
258
|
-
return {
|
|
259
|
-
root,
|
|
260
|
-
tree: rootNode,
|
|
261
|
-
truncated,
|
|
262
|
-
totalEntries,
|
|
263
|
-
};
|
|
264
|
-
});
|
|
265
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export declare function mergeOptions<T extends object>(defaults: T, overrides: Partial<T>): T;
|
|
2
|
-
export declare function omitOptionKeys<T extends object, K extends keyof T>(input: T, keys: readonly K[]): Omit<T, K>;
|
|
3
|
-
export declare function setIfDefined<T extends object, K extends keyof T>(target: T, key: K, value: T[K] | undefined): void;
|
package/dist/lib/option-utils.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function mergeOptions(defaults, overrides) {
|
|
2
|
-
return { ...defaults, ...overrides };
|
|
3
|
-
}
|
|
4
|
-
export function omitOptionKeys(input, keys) {
|
|
5
|
-
const output = { ...input };
|
|
6
|
-
for (const key of keys) {
|
|
7
|
-
Reflect.deleteProperty(output, key);
|
|
8
|
-
}
|
|
9
|
-
return output;
|
|
10
|
-
}
|
|
11
|
-
export function setIfDefined(target, key, value) {
|
|
12
|
-
if (value !== undefined) {
|
|
13
|
-
target[key] = value;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function toPosixPath(value: string): string;
|
package/dist/lib/path-format.js
DELETED
package/dist/lib/path-policy.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
|
-
import { platform } from 'node:os';
|
|
3
|
-
import { SENSITIVE_FILE_ALLOWLIST, SENSITIVE_FILE_DENYLIST, } from './constants.js';
|
|
4
|
-
import { ErrorCode, McpError } from './errors.js';
|
|
5
|
-
import { toPosixPath } from './path-format.js';
|
|
6
|
-
const IS_WINDOWS = platform() === 'win32';
|
|
7
|
-
const WINDOWS_ABSOLUTE_RE = /^[a-z]:\//iu;
|
|
8
|
-
function normalizePathForMatch(input) {
|
|
9
|
-
return toPosixPath(path.normalize(input));
|
|
10
|
-
}
|
|
11
|
-
function normalizeForMatch(input) {
|
|
12
|
-
const normalized = normalizePathForMatch(input);
|
|
13
|
-
return IS_WINDOWS ? normalized.toLowerCase() : normalized;
|
|
14
|
-
}
|
|
15
|
-
function compilePatternGlobs(normalizedPattern) {
|
|
16
|
-
const globs = new Set([normalizedPattern]);
|
|
17
|
-
const isWindowsAbsolute = WINDOWS_ABSOLUTE_RE.test(normalizedPattern);
|
|
18
|
-
if (!normalizedPattern.startsWith('**/') && !isWindowsAbsolute) {
|
|
19
|
-
const withoutRoot = normalizedPattern.replace(/^\/+/u, '');
|
|
20
|
-
if (withoutRoot.length > 0) {
|
|
21
|
-
globs.add(`**/${withoutRoot}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return [...globs];
|
|
25
|
-
}
|
|
26
|
-
function compilePatterns(patterns) {
|
|
27
|
-
const unique = new Set();
|
|
28
|
-
for (const pattern of patterns) {
|
|
29
|
-
const trimmed = pattern.trim();
|
|
30
|
-
if (trimmed.length > 0) {
|
|
31
|
-
unique.add(trimmed);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const compiled = [];
|
|
35
|
-
for (const pattern of unique) {
|
|
36
|
-
const normalized = normalizeForMatch(pattern);
|
|
37
|
-
const matchesPath = normalized.includes('/');
|
|
38
|
-
compiled.push({
|
|
39
|
-
globs: matchesPath ? compilePatternGlobs(normalized) : [normalized],
|
|
40
|
-
matchesPath,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
return compiled;
|
|
44
|
-
}
|
|
45
|
-
function toPatternSet(patterns) {
|
|
46
|
-
const pathGlobs = new Set();
|
|
47
|
-
const nameGlobs = new Set();
|
|
48
|
-
for (const pattern of patterns) {
|
|
49
|
-
const target = pattern.matchesPath ? pathGlobs : nameGlobs;
|
|
50
|
-
for (const glob of pattern.globs) {
|
|
51
|
-
target.add(glob);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
pathGlobs: [...pathGlobs],
|
|
56
|
-
nameGlobs: [...nameGlobs],
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
const DENY_PATTERNS = toPatternSet(compilePatterns(SENSITIVE_FILE_DENYLIST));
|
|
60
|
-
const ALLOW_PATTERNS = toPatternSet(compilePatterns(SENSITIVE_FILE_ALLOWLIST));
|
|
61
|
-
function uniquePair(primary, secondary) {
|
|
62
|
-
if (!secondary || secondary === primary)
|
|
63
|
-
return [primary];
|
|
64
|
-
return [primary, secondary];
|
|
65
|
-
}
|
|
66
|
-
function matchesAnyGlobs(globs, candidates) {
|
|
67
|
-
if (globs.length === 0 || candidates.length === 0)
|
|
68
|
-
return false;
|
|
69
|
-
for (const candidate of candidates) {
|
|
70
|
-
for (const glob of globs) {
|
|
71
|
-
if (path.posix.matchesGlob(candidate, glob))
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
export function isSensitivePath(requestedPath, resolvedPath) {
|
|
78
|
-
if (DENY_PATTERNS.pathGlobs.length === 0 &&
|
|
79
|
-
DENY_PATTERNS.nameGlobs.length === 0) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
const normalizedRequested = normalizeForMatch(requestedPath);
|
|
83
|
-
const normalizedResolved = resolvedPath
|
|
84
|
-
? normalizeForMatch(resolvedPath)
|
|
85
|
-
: undefined;
|
|
86
|
-
const pathCandidates = uniquePair(normalizedRequested, normalizedResolved);
|
|
87
|
-
const nameCandidates = uniquePair(path.posix.basename(normalizedRequested), normalizedResolved ? path.posix.basename(normalizedResolved) : undefined);
|
|
88
|
-
if (matchesAnyGlobs(ALLOW_PATTERNS.pathGlobs, pathCandidates) ||
|
|
89
|
-
matchesAnyGlobs(ALLOW_PATTERNS.nameGlobs, nameCandidates)) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
return (matchesAnyGlobs(DENY_PATTERNS.pathGlobs, pathCandidates) ||
|
|
93
|
-
matchesAnyGlobs(DENY_PATTERNS.nameGlobs, nameCandidates));
|
|
94
|
-
}
|
|
95
|
-
export function assertAllowedFileAccess(requestedPath, resolvedPath) {
|
|
96
|
-
if (!isSensitivePath(requestedPath, resolvedPath))
|
|
97
|
-
return;
|
|
98
|
-
throw new McpError(ErrorCode.E_ACCESS_DENIED, `Access denied: sensitive file blocked by policy (${requestedPath}). ` +
|
|
99
|
-
'Set FS_CONTEXT_ALLOW_SENSITIVE=1 or use FS_CONTEXT_ALLOWLIST to override.', requestedPath);
|
|
100
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface ProgressPayload {
|
|
2
|
-
current: number;
|
|
3
|
-
total?: number;
|
|
4
|
-
}
|
|
5
|
-
export type ProgressCallback = ((progress: ProgressPayload) => void) | undefined;
|
|
6
|
-
export interface PeriodicProgressOptions {
|
|
7
|
-
total?: number;
|
|
8
|
-
throttleModulo?: number;
|
|
9
|
-
force?: boolean;
|
|
10
|
-
}
|
|
11
|
-
export declare function reportPeriodicProgress(onProgress: ProgressCallback, current: number, options?: PeriodicProgressOptions): void;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export function reportPeriodicProgress(onProgress, current, options = {}) {
|
|
2
|
-
if (!onProgress || current === 0)
|
|
3
|
-
return;
|
|
4
|
-
const throttleModulo = options.throttleModulo ?? 1;
|
|
5
|
-
const force = options.force ?? false;
|
|
6
|
-
if (!force && throttleModulo > 1 && current % throttleModulo !== 0) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
onProgress({
|
|
10
|
-
current,
|
|
11
|
-
...(options.total !== undefined ? { total: options.total } : {}),
|
|
12
|
-
});
|
|
13
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
package/dist/lib/type-guards.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
interface CapabilityOptions {
|
|
3
|
-
enablePromptListChanged?: boolean;
|
|
4
|
-
enableTaskToolRequests?: boolean;
|
|
5
|
-
}
|
|
6
|
-
type ServerCapabilities = NonNullable<ConstructorParameters<typeof McpServer>[1]>['capabilities'];
|
|
7
|
-
type NonOptionalServerCapabilities = NonNullable<ServerCapabilities>;
|
|
8
|
-
export declare function buildServerCapabilities(options?: CapabilityOptions): NonOptionalServerCapabilities;
|
|
9
|
-
export declare function supportsTaskToolRequests(): boolean;
|
|
10
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
let cachedTaskToolSupport;
|
|
3
|
-
function detectTaskToolSupport() {
|
|
4
|
-
if (cachedTaskToolSupport !== undefined) {
|
|
5
|
-
return cachedTaskToolSupport;
|
|
6
|
-
}
|
|
7
|
-
try {
|
|
8
|
-
// Instantiate a minimal, unconnected probe server to duck-type check for
|
|
9
|
-
// task tool support. The probe has no transport or active connections, so
|
|
10
|
-
// close() only releases in-memory state; fire-and-forget is safe here.
|
|
11
|
-
const probe = new McpServer({
|
|
12
|
-
name: 'filesystem-mcp-capability-probe',
|
|
13
|
-
version: '0.0.0',
|
|
14
|
-
}, { capabilities: { tools: {} } });
|
|
15
|
-
cachedTaskToolSupport =
|
|
16
|
-
typeof probe.experimental.tasks.registerToolTask === 'function';
|
|
17
|
-
probe.close().catch(() => { });
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
cachedTaskToolSupport = false;
|
|
21
|
-
}
|
|
22
|
-
return cachedTaskToolSupport;
|
|
23
|
-
}
|
|
24
|
-
export function buildServerCapabilities(options = {}) {
|
|
25
|
-
const capabilities = {
|
|
26
|
-
logging: {},
|
|
27
|
-
resources: {},
|
|
28
|
-
tools: {},
|
|
29
|
-
prompts: options.enablePromptListChanged ? { listChanged: true } : {},
|
|
30
|
-
completions: {},
|
|
31
|
-
};
|
|
32
|
-
if (options.enableTaskToolRequests) {
|
|
33
|
-
// NOTE: enabling task tool requests requires the caller to configure
|
|
34
|
-
// an InMemoryTaskStore and InMemoryTaskMessageQueue on the McpServer.
|
|
35
|
-
// InMemoryTaskStore accumulates completed task records with no TTL eviction —
|
|
36
|
-
// suitable for short-lived stdio sessions. Long-running HTTP servers should
|
|
37
|
-
// replace it with a TTL-evicting store to avoid unbounded memory growth.
|
|
38
|
-
capabilities.tasks = {
|
|
39
|
-
list: {},
|
|
40
|
-
cancel: {},
|
|
41
|
-
requests: { tools: { call: {} } },
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return capabilities;
|
|
45
|
-
}
|
|
46
|
-
export function supportsTaskToolRequests() {
|
|
47
|
-
return detectTaskToolSupport();
|
|
48
|
-
}
|
package/dist/server/logging.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
-
export interface LoggingState {
|
|
4
|
-
minimumLevel: LoggingLevel;
|
|
5
|
-
}
|
|
6
|
-
export declare function createLoggingState(minimumLevel?: LoggingLevel): LoggingState;
|
|
7
|
-
export declare function logToMcp(server: McpServer | undefined, level: LoggingLevel, data: string, minLevel?: LoggingLevel): void;
|
package/dist/server/logging.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { formatUnknownErrorMessage } from '../lib/errors.js';
|
|
2
|
-
import { isRecord } from '../lib/type-guards.js';
|
|
3
|
-
const MCP_LOGGER_NAME = 'filesystem-mcp';
|
|
4
|
-
const LOG_LEVEL_ORDER = {
|
|
5
|
-
debug: 0,
|
|
6
|
-
info: 1,
|
|
7
|
-
notice: 2,
|
|
8
|
-
warning: 3,
|
|
9
|
-
error: 4,
|
|
10
|
-
critical: 5,
|
|
11
|
-
alert: 6,
|
|
12
|
-
emergency: 7,
|
|
13
|
-
};
|
|
14
|
-
export function createLoggingState(minimumLevel = 'debug') {
|
|
15
|
-
return { minimumLevel };
|
|
16
|
-
}
|
|
17
|
-
function canSendMcpLogs(server) {
|
|
18
|
-
const capabilities = server.server.getClientCapabilities();
|
|
19
|
-
if (!isRecord(capabilities))
|
|
20
|
-
return false;
|
|
21
|
-
if (!('logging' in capabilities))
|
|
22
|
-
return false;
|
|
23
|
-
return !!capabilities['logging'];
|
|
24
|
-
}
|
|
25
|
-
export function logToMcp(server, level, data, minLevel = 'debug') {
|
|
26
|
-
if (LOG_LEVEL_ORDER[level] < LOG_LEVEL_ORDER[minLevel]) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (!server || !canSendMcpLogs(server)) {
|
|
30
|
-
console.error(data);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const params = {
|
|
34
|
-
level,
|
|
35
|
-
logger: MCP_LOGGER_NAME,
|
|
36
|
-
data,
|
|
37
|
-
};
|
|
38
|
-
void server.sendLoggingMessage(params).catch((error) => {
|
|
39
|
-
console.error(`Failed to send MCP log: ${level} | ${data}`, formatUnknownErrorMessage(error));
|
|
40
|
-
});
|
|
41
|
-
}
|
package/dist/server/types.d.ts
DELETED
package/dist/server/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|