@j0hanz/filesystem-mcp 1.14.0 → 1.14.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.
Files changed (66) hide show
  1. package/README.md +47 -47
  2. package/dist/cli.js +2 -2
  3. package/dist/completions.js +54 -51
  4. package/dist/config.d.ts +13 -13
  5. package/dist/config.js +12 -12
  6. package/dist/index.js +1 -1
  7. package/dist/lib/abort.d.ts +7 -0
  8. package/dist/lib/abort.js +81 -0
  9. package/dist/lib/constants.d.ts +3 -1
  10. package/dist/lib/constants.js +8 -2
  11. package/dist/lib/errors.d.ts +7 -3
  12. package/dist/lib/errors.js +59 -39
  13. package/dist/lib/file-operations/core.d.ts +3 -3
  14. package/dist/lib/file-operations/core.js +23 -20
  15. package/dist/lib/file-operations/metadata.d.ts +2 -2
  16. package/dist/lib/file-operations/metadata.js +60 -19
  17. package/dist/lib/file-operations/search.js +83 -84
  18. package/dist/lib/file-operations/traversal.js +13 -15
  19. package/dist/lib/fs-helpers.d.ts +3 -10
  20. package/dist/lib/fs-helpers.js +20 -98
  21. package/dist/lib/globs.js +1 -1
  22. package/dist/lib/logger.d.ts +28 -0
  23. package/dist/lib/logger.js +91 -0
  24. package/dist/lib/observability.d.ts +7 -0
  25. package/dist/lib/observability.js +19 -9
  26. package/dist/lib/paths.js +55 -55
  27. package/dist/lib/resource-store.js +4 -4
  28. package/dist/lib/utils.d.ts +0 -12
  29. package/dist/lib/utils.js +0 -13
  30. package/dist/resources/generated-instructions.js +40 -31
  31. package/dist/resources/tool-catalog.js +32 -26
  32. package/dist/resources/tool-info.js +34 -29
  33. package/dist/resources/workflows.js +39 -18
  34. package/dist/resources.d.ts +1 -1
  35. package/dist/resources.js +4 -4
  36. package/dist/schemas.d.ts +66 -66
  37. package/dist/schemas.js +21 -44
  38. package/dist/server/bootstrap.d.ts +12 -11
  39. package/dist/server/bootstrap.js +95 -86
  40. package/dist/server/roots-manager.d.ts +5 -2
  41. package/dist/server/roots-manager.js +8 -6
  42. package/dist/server/task-store.d.ts +10 -0
  43. package/dist/server/task-store.js +73 -0
  44. package/dist/tools/apply-patch.js +26 -18
  45. package/dist/tools/calculate-hash.js +11 -22
  46. package/dist/tools/create-directory.js +10 -8
  47. package/dist/tools/delete-file.js +17 -15
  48. package/dist/tools/diff-files.js +15 -15
  49. package/dist/tools/edit-file.js +7 -4
  50. package/dist/tools/list-directory.js +6 -6
  51. package/dist/tools/move-file.js +98 -81
  52. package/dist/tools/read-multiple.js +4 -4
  53. package/dist/tools/read.js +5 -5
  54. package/dist/tools/replace-in-files.js +20 -23
  55. package/dist/tools/roots.js +1 -1
  56. package/dist/tools/search-content.js +32 -41
  57. package/dist/tools/search-files.js +54 -41
  58. package/dist/tools/shared.d.ts +3 -0
  59. package/dist/tools/shared.js +70 -28
  60. package/dist/tools/stat-many.js +11 -7
  61. package/dist/tools/stat.js +4 -4
  62. package/dist/tools/task-support.d.ts +10 -9
  63. package/dist/tools/task-support.js +94 -23
  64. package/dist/tools/tree.js +3 -3
  65. package/dist/tools/write-file.js +10 -7
  66. package/package.json +9 -9
@@ -0,0 +1,28 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
3
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
+ export interface SessionContextData {
5
+ sessionId?: string;
6
+ }
7
+ export declare const SessionContext: AsyncLocalStorage<SessionContextData>;
8
+ export interface LogEvent {
9
+ level: LoggingLevel;
10
+ message: string;
11
+ data?: unknown;
12
+ sessionId?: string;
13
+ }
14
+ export interface LoggingState {
15
+ minimumLevel: LoggingLevel;
16
+ }
17
+ export declare function createLoggingState(minimumLevel?: LoggingLevel): LoggingState;
18
+ export declare function canSendMcpLogs(server: McpServer): boolean;
19
+ export declare function logToMcp(server: McpServer | undefined, level: LoggingLevel, data: string, minLevel?: LoggingLevel): void;
20
+ export declare const Logger: {
21
+ emit(level: LoggingLevel, message: string, data?: unknown): void;
22
+ debug(message: string, data?: unknown): void;
23
+ info(message: string, data?: unknown): void;
24
+ notice(message: string, data?: unknown): void;
25
+ warn(message: string, data?: unknown): void;
26
+ error(message: string, data?: unknown): void;
27
+ critical(message: string, data?: unknown): void;
28
+ };
@@ -0,0 +1,91 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { channel } from 'node:diagnostics_channel';
3
+ export const SessionContext = new AsyncLocalStorage();
4
+ const LOG_CHANNEL = channel('filesystem-mcp:log');
5
+ const MCP_LOGGER_NAME = 'filesystem-mcp';
6
+ const LOG_LEVEL_ORDER = {
7
+ debug: 0,
8
+ info: 1,
9
+ notice: 2,
10
+ warning: 3,
11
+ error: 4,
12
+ critical: 5,
13
+ alert: 6,
14
+ emergency: 7,
15
+ };
16
+ export function createLoggingState(minimumLevel = 'debug') {
17
+ return { minimumLevel };
18
+ }
19
+ export function canSendMcpLogs(server) {
20
+ const capabilities = server.server.getClientCapabilities();
21
+ if (!capabilities || typeof capabilities !== 'object')
22
+ return false;
23
+ return 'logging' in capabilities && Boolean(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(`[${level.toUpperCase()}] ${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}`, formatTransportError(error));
40
+ });
41
+ }
42
+ function formatTransportError(error) {
43
+ if (error instanceof Error)
44
+ return error.message;
45
+ if (typeof error === 'string')
46
+ return error;
47
+ try {
48
+ return JSON.stringify(error);
49
+ }
50
+ catch {
51
+ return String(error);
52
+ }
53
+ }
54
+ export const Logger = {
55
+ emit(level, message, data) {
56
+ const session = SessionContext.getStore();
57
+ const event = {
58
+ level,
59
+ message,
60
+ ...(data !== undefined ? { data } : {}),
61
+ ...(session?.sessionId !== undefined
62
+ ? { sessionId: session.sessionId }
63
+ : {}),
64
+ };
65
+ if (LOG_CHANNEL.hasSubscribers) {
66
+ LOG_CHANNEL.publish(event);
67
+ }
68
+ else {
69
+ // Fallback if no subscribers
70
+ console.error(`[${level.toUpperCase()}] ${message}`, data ?? '');
71
+ }
72
+ },
73
+ debug(message, data) {
74
+ this.emit('debug', message, data);
75
+ },
76
+ info(message, data) {
77
+ this.emit('info', message, data);
78
+ },
79
+ notice(message, data) {
80
+ this.emit('notice', message, data);
81
+ },
82
+ warn(message, data) {
83
+ this.emit('warning', message, data);
84
+ },
85
+ error(message, data) {
86
+ this.emit('error', message, data);
87
+ },
88
+ critical(message, data) {
89
+ this.emit('critical', message, data);
90
+ },
91
+ };
@@ -5,6 +5,11 @@ interface OpsTraceContext {
5
5
  path?: string | undefined;
6
6
  [key: string]: unknown;
7
7
  }
8
+ export interface TraceContext {
9
+ traceparent: string;
10
+ tracestate?: string;
11
+ baggage?: string;
12
+ }
8
13
  interface ToolMetrics {
9
14
  calls: number;
10
15
  errors: number;
@@ -19,8 +24,10 @@ export declare function getToolContextSnapshot(): {
19
24
  tool: string;
20
25
  path?: string;
21
26
  } | undefined;
27
+ export declare function getTraceContext(): TraceContext | undefined;
22
28
  export declare function startPerfMeasure(name: string, detail?: Record<string, unknown>): ((ok?: boolean) => void) | undefined;
23
29
  export declare function withToolDiagnostics<T>(tool: string, run: () => Promise<T>, options?: {
24
30
  path?: string;
31
+ traceContext?: TraceContext;
25
32
  }): Promise<T>;
26
33
  export {};
@@ -3,16 +3,18 @@ import { hash } from 'node:crypto';
3
3
  import { channel, tracingChannel } from 'node:diagnostics_channel';
4
4
  import { monitorEventLoopDelay, performance, PerformanceObserver, } from 'node:perf_hooks';
5
5
  import { parseTrueEnvFlag } from './constants.js';
6
+ import { Logger } from './logger.js';
6
7
  import { isRecord } from './utils.js';
7
8
  // --- Configuration ---
8
9
  const ENV = process.env;
9
10
  let _cachedConfig;
10
11
  function readConfig() {
11
- return (_cachedConfig ??= {
12
+ _cachedConfig ??= {
12
13
  enabled: parseTrueEnvFlag(ENV['FS_CONTEXT_DIAGNOSTICS']),
13
14
  detail: parseDetail(ENV['FS_CONTEXT_DIAGNOSTICS_DETAIL']),
14
15
  logToolErrors: parseTrueEnvFlag(ENV['FS_CONTEXT_TOOL_LOG_ERRORS']),
15
- });
16
+ };
17
+ return _cachedConfig;
16
18
  }
17
19
  function parseDetail(val) {
18
20
  if (val === '2')
@@ -201,6 +203,9 @@ function applyToolContext(context) {
201
203
  export function getToolContextSnapshot() {
202
204
  return toolContext.getStore();
203
205
  }
206
+ export function getTraceContext() {
207
+ return toolContext.getStore()?.traceContext;
208
+ }
204
209
  function normalizeContext(ctx) {
205
210
  if (!ctx.path)
206
211
  return ctx;
@@ -254,16 +259,20 @@ export function startPerfMeasure(name, detail) {
254
259
  }
255
260
  };
256
261
  }
257
- function publishToolStart(tool, pathVal) {
262
+ function publishToolStart(tool, pathVal, traceparent) {
258
263
  const event = { phase: 'start', tool };
259
264
  if (pathVal)
260
265
  event.path = pathVal;
266
+ if (traceparent)
267
+ event.traceparent = traceparent;
261
268
  CHANNELS.tool.publish(event);
262
269
  }
263
- function publishToolEnd(tool, ok, durationMs, errorMsg) {
270
+ function publishToolEnd(tool, ok, durationMs, errorMsg, traceparent) {
264
271
  const event = { phase: 'end', tool, ok, durationMs };
265
272
  if (errorMsg)
266
273
  event.error = errorMsg;
274
+ if (traceparent)
275
+ event.traceparent = traceparent;
267
276
  CHANNELS.tool.publish(event);
268
277
  }
269
278
  function publishPerfEnd(tool, durationMs, eluStart, loopMonitor) {
@@ -281,13 +290,13 @@ function publishPerfEnd(tool, durationMs, eluStart, loopMonitor) {
281
290
  }
282
291
  CHANNELS.perf.publish(event);
283
292
  }
284
- async function runAndObserve(tool, run, pubTool, pubPerf, logErrors, pathVal) {
293
+ async function runAndObserve(tool, run, pubTool, pubPerf, logErrors, pathVal, traceparent) {
285
294
  const startMs = performance.now();
286
295
  const eluStart = pubPerf ? performance.eventLoopUtilization() : undefined;
287
296
  const loopMonitor = pubPerf ? monitorEventLoopDelay() : undefined;
288
297
  loopMonitor?.enable();
289
298
  if (pubTool)
290
- publishToolStart(tool, pathVal);
299
+ publishToolStart(tool, pathVal, traceparent);
291
300
  let result;
292
301
  const obs = { ok: false, errorMsg: undefined };
293
302
  try {
@@ -306,7 +315,7 @@ async function runAndObserve(tool, run, pubTool, pubPerf, logErrors, pathVal) {
306
315
  if (pubPerf && eluStart)
307
316
  publishPerfEnd(tool, durationMs, eluStart, loopMonitor);
308
317
  if (pubTool)
309
- publishToolEnd(tool, obs.ok, durationMs, obs.errorMsg);
318
+ publishToolEnd(tool, obs.ok, durationMs, obs.errorMsg, traceparent);
310
319
  updateMetrics(tool, obs.ok, durationMs);
311
320
  if (logErrors && !obs.ok)
312
321
  logError(tool, durationMs, obs.errorMsg);
@@ -319,6 +328,7 @@ export async function withToolDiagnostics(tool, run, options) {
319
328
  const context = {
320
329
  tool,
321
330
  ...(options?.path ? { path: options.path } : {}),
331
+ ...(options?.traceContext ? { traceContext: options.traceContext } : {}),
322
332
  };
323
333
  return toolContext.run(context, async () => {
324
334
  if (!config.enabled) {
@@ -354,10 +364,10 @@ export async function withToolDiagnostics(tool, run, options) {
354
364
  throw e;
355
365
  }
356
366
  }
357
- return runAndObserve(tool, run, pubTool, pubPerf, config.logToolErrors, normalizedPath);
367
+ return runAndObserve(tool, run, pubTool, pubPerf, config.logToolErrors, normalizedPath, options?.traceContext?.traceparent);
358
368
  });
359
369
  }
360
370
  function logError(tool, durationMs, msg) {
361
371
  const suffix = msg ? `: ${msg}` : '';
362
- console.error(`[ToolError] ${tool} failed in ${durationMs.toFixed(1)}ms${suffix}`);
372
+ Logger.error(`[ToolError] ${tool} failed in ${durationMs.toFixed(1)}ms${suffix}`);
363
373
  }
package/dist/lib/paths.js CHANGED
@@ -1,12 +1,12 @@
1
- import * as fs from 'node:fs/promises';
2
- import * as os from 'node:os';
3
- import * as path from 'node:path';
4
1
  import { AsyncLocalStorage } from 'node:async_hooks';
5
- import { platform } from 'node:os';
2
+ import { realpath, stat } from 'node:fs/promises';
3
+ import { homedir, platform } from 'node:os';
4
+ import { dirname, isAbsolute, join, normalize, parse, posix, relative, resolve, sep, win32, } from 'node:path';
6
5
  import { fileURLToPath } from 'node:url';
6
+ import { assertNotAborted, withAbort } from './abort.js';
7
7
  import { SENSITIVE_FILE_ALLOWLIST, SENSITIVE_FILE_DENYLIST, } from './constants.js';
8
8
  import { ErrorCode, isAbortError, isNodeError, McpError } from './errors.js';
9
- import { assertNotAborted, withAbort } from './fs-helpers.js';
9
+ import { Logger } from './logger.js';
10
10
  const WINDOWS_PATH_SEPARATOR = '\\';
11
11
  const POSIX_PATH_SEPARATOR = '/';
12
12
  export function toPosixPath(value) {
@@ -20,7 +20,7 @@ const HOME_PREFIX_LENGTH = 2;
20
20
  const CHAR_CODE_SPACE = 32;
21
21
  const CHAR_CODE_DOT = 46;
22
22
  function normalizePathForMatch(input) {
23
- return toPosixPath(path.normalize(input));
23
+ return toPosixPath(normalize(input));
24
24
  }
25
25
  function normalizeForMatch(input) {
26
26
  const normalized = normalizePathForMatch(input);
@@ -84,7 +84,7 @@ function matchesAnyGlobs(globs, candidates) {
84
84
  return false;
85
85
  for (const candidate of candidates) {
86
86
  for (const glob of globs) {
87
- if (path.posix.matchesGlob(candidate, glob))
87
+ if (posix.matchesGlob(candidate, glob))
88
88
  return true;
89
89
  }
90
90
  }
@@ -100,7 +100,7 @@ export function isSensitivePath(requestedPath, resolvedPath) {
100
100
  ? normalizeForMatch(resolvedPath)
101
101
  : undefined;
102
102
  const pathCandidates = uniquePair(normalizedRequested, normalizedResolved);
103
- const nameCandidates = uniquePair(path.posix.basename(normalizedRequested), normalizedResolved ? path.posix.basename(normalizedResolved) : undefined);
103
+ const nameCandidates = uniquePair(posix.basename(normalizedRequested), normalizedResolved ? posix.basename(normalizedResolved) : undefined);
104
104
  if (matchesAnyGlobs(ALLOW_PATTERNS.pathGlobs, pathCandidates) ||
105
105
  matchesAnyGlobs(ALLOW_PATTERNS.nameGlobs, nameCandidates)) {
106
106
  return false;
@@ -111,11 +111,11 @@ export function isSensitivePath(requestedPath, resolvedPath) {
111
111
  export function assertAllowedFileAccess(requestedPath, resolvedPath) {
112
112
  if (!isSensitivePath(requestedPath, resolvedPath))
113
113
  return;
114
- throw new McpError(ErrorCode.E_ACCESS_DENIED, `Access denied: sensitive file blocked by policy (${requestedPath}). ` +
115
- 'Set FS_CONTEXT_ALLOW_SENSITIVE=1 or use FS_CONTEXT_ALLOWLIST to override.', requestedPath);
114
+ Logger.warn(`Access denied: sensitive file blocked by policy (${requestedPath})`);
115
+ throw new McpError(ErrorCode.ACCESS_DENIED, 'Sensitive file blocked. Set FS_CONTEXT_ALLOW_SENSITIVE=1 to override.', requestedPath);
116
116
  }
117
- const HOMEDIR = os.homedir();
118
- const PATH_SEPARATOR = path.sep;
117
+ const HOMEDIR = homedir();
118
+ const PATH_SEPARATOR = sep;
119
119
  const DRIVE_LETTER_REGEX = /^[A-Za-z]:/;
120
120
  const WINDOWS_DRIVE_REL_REGEX = /^[A-Za-z]:$/u;
121
121
  const LEADING_SEPARATORS_RE = /^[/\\]+/;
@@ -160,11 +160,11 @@ function expandHome(filepath) {
160
160
  return HOMEDIR;
161
161
  // Accept both "~/" and "~\\" for cross-platform UX.
162
162
  if (filepath.startsWith('~/') || filepath.startsWith('~\\')) {
163
- // Avoid `path.join(HOMEDIR, "/foo")` resetting to the filesystem root.
163
+ // Avoid `join(HOMEDIR, "/foo")` resetting to the filesystem root.
164
164
  const rest = filepath
165
165
  .slice(HOME_PREFIX_LENGTH)
166
166
  .replace(LEADING_SEPARATORS_RE, '');
167
- return rest.length === 0 ? HOMEDIR : path.join(HOMEDIR, rest);
167
+ return rest.length === 0 ? HOMEDIR : join(HOMEDIR, rest);
168
168
  }
169
169
  return filepath;
170
170
  }
@@ -175,7 +175,7 @@ function expandHome(filepath) {
175
175
  * - Lowercases Windows drive letter for stable comparisons.
176
176
  */
177
177
  export function normalizePath(p) {
178
- const resolved = path.resolve(expandHome(p));
178
+ const resolved = resolve(expandHome(p));
179
179
  if (IS_WINDOWS && DRIVE_LETTER_REGEX.test(resolved)) {
180
180
  return resolved.charAt(0).toLowerCase() + resolved.slice(1);
181
181
  }
@@ -194,8 +194,8 @@ function rethrowIfAborted(error) {
194
194
  function isSamePath(left, right) {
195
195
  if (left === right)
196
196
  return true;
197
- const leftResolved = normalizeCaseForComparison(path.resolve(left));
198
- const rightResolved = normalizeCaseForComparison(path.resolve(right));
197
+ const leftResolved = normalizeCaseForComparison(resolve(left));
198
+ const rightResolved = normalizeCaseForComparison(resolve(right));
199
199
  return leftResolved === rightResolved;
200
200
  }
201
201
  function stripTrailingSeparator(normalized) {
@@ -211,7 +211,7 @@ function normalizeAllowedDirectory(dir) {
211
211
  if (trimmed.length === 0)
212
212
  return '';
213
213
  const normalized = normalizePath(trimmed);
214
- const { root } = path.parse(normalized);
214
+ const { root } = parse(normalized);
215
215
  // Keep filesystem roots as-is ("/", "c:\\", "\\\\server\\share\\").
216
216
  if (isFileSystemRootPath(normalized, root)) {
217
217
  return root;
@@ -274,14 +274,12 @@ function isPathInsideDirectory(normalizedDirectory, normalizedCandidate) {
274
274
  const candidate = normalizeForComparison(normalizedCandidate);
275
275
  if (root === candidate)
276
276
  return true;
277
- const relative = path.relative(root, candidate);
278
- if (relative.length === 0)
277
+ const rel = relative(root, candidate);
278
+ if (rel.length === 0)
279
279
  return true;
280
- if (relative === '..')
280
+ if (rel === '..')
281
281
  return false;
282
- return (!relative.startsWith('..\\') &&
283
- !relative.startsWith('../') &&
284
- !path.isAbsolute(relative));
282
+ return !rel.startsWith('..\\') && !rel.startsWith('../') && !isAbsolute(rel);
285
283
  }
286
284
  export function isPathWithinDirectories(normalizedPath, allowedDirs) {
287
285
  for (const allowedDir of allowedDirs) {
@@ -293,7 +291,7 @@ export function isPathWithinDirectories(normalizedPath, allowedDirs) {
293
291
  async function resolveRealPath(normalized, signal) {
294
292
  try {
295
293
  assertNotAborted(signal);
296
- const realPath = await withAbort(fs.realpath(normalized), signal);
294
+ const realPath = await withAbort(realpath(normalized), signal);
297
295
  return normalizeAllowedDirectory(realPath);
298
296
  }
299
297
  catch (error) {
@@ -327,12 +325,12 @@ export async function setAllowedDirectoriesResolved(dirs, signal) {
327
325
  }
328
326
  function ensureNonEmptyPath(requestedPath) {
329
327
  if (!requestedPath || requestedPath.trim().length === 0) {
330
- throw new McpError(ErrorCode.E_INVALID_INPUT, 'Path cannot be empty or whitespace', requestedPath);
328
+ throw new McpError(ErrorCode.INVALID_INPUT, 'Path cannot be empty or whitespace', requestedPath);
331
329
  }
332
330
  }
333
331
  function ensureNoNullBytes(requestedPath) {
334
332
  if (requestedPath.includes('\0')) {
335
- throw new McpError(ErrorCode.E_INVALID_INPUT, 'Path contains null bytes', requestedPath);
333
+ throw new McpError(ErrorCode.INVALID_INPUT, 'Path contains null bytes', requestedPath);
336
334
  }
337
335
  }
338
336
  function getReservedDeviceName(segment) {
@@ -371,31 +369,31 @@ function ensureNoReservedWindowsNames(requestedPath) {
371
369
  const reserved = getReservedDeviceNameForPath(requestedPath);
372
370
  if (!reserved)
373
371
  return;
374
- throw new McpError(ErrorCode.E_INVALID_INPUT, `Windows reserved device name not allowed: ${reserved}`, requestedPath);
372
+ throw new McpError(ErrorCode.INVALID_INPUT, `Windows reserved device name not allowed: ${reserved}`, requestedPath);
375
373
  }
376
374
  export function isWindowsDriveRelativePath(requestedPath) {
377
375
  if (!IS_WINDOWS)
378
376
  return false;
379
- const parsed = path.win32.parse(requestedPath);
377
+ const parsed = win32.parse(requestedPath);
380
378
  if (!WINDOWS_DRIVE_REL_REGEX.test(parsed.root))
381
379
  return false;
382
- return !path.win32.isAbsolute(requestedPath);
380
+ return !win32.isAbsolute(requestedPath);
383
381
  }
384
382
  function ensureNoWindowsDriveRelativePath(requestedPath) {
385
383
  if (!isWindowsDriveRelativePath(requestedPath))
386
384
  return;
387
- throw new McpError(ErrorCode.E_INVALID_INPUT, 'Windows drive-relative paths are not allowed. Use C:\\path or C:/path instead of C:path.', requestedPath);
385
+ throw new McpError(ErrorCode.INVALID_INPUT, 'Drive-relative path not allowed. Use C:\\path instead of C:path.', requestedPath);
388
386
  }
389
387
  function resolveRequestedPath(requestedPath) {
390
388
  const expanded = expandHome(requestedPath);
391
- if (!path.isAbsolute(expanded)) {
389
+ if (!isAbsolute(expanded)) {
392
390
  const roots = getAllowedDirectoriesForRelativeResolution();
393
391
  if (roots.length > 1) {
394
- throw new McpError(ErrorCode.E_INVALID_INPUT, 'Relative paths are ambiguous when multiple roots are configured. Provide an absolute path or specify the full root path.', requestedPath);
392
+ throw new McpError(ErrorCode.INVALID_INPUT, 'Ambiguous relative path with multiple roots. Use an absolute path.', requestedPath);
395
393
  }
396
394
  const baseDir = roots[0];
397
395
  if (baseDir) {
398
- return normalizePath(path.resolve(baseDir, expanded));
396
+ return normalizePath(resolve(baseDir, expanded));
399
397
  }
400
398
  }
401
399
  return normalizePath(expanded);
@@ -409,24 +407,24 @@ function validateRequestedPath(requestedPath) {
409
407
  }
410
408
  const NODE_ERROR_MAP = {
411
409
  ENOENT: {
412
- code: ErrorCode.E_NOT_FOUND,
413
- message: (requestedPath) => `Path does not exist: ${requestedPath}`,
410
+ code: ErrorCode.NOT_FOUND,
411
+ message: () => 'Path does not exist',
414
412
  },
415
413
  EACCES: {
416
- code: ErrorCode.E_PERMISSION_DENIED,
417
- message: (requestedPath) => `Permission denied accessing path: ${requestedPath}`,
414
+ code: ErrorCode.PERMISSION_DENIED,
415
+ message: () => 'Permission denied',
418
416
  },
419
417
  EPERM: {
420
- code: ErrorCode.E_PERMISSION_DENIED,
421
- message: (requestedPath) => `Permission denied accessing path: ${requestedPath}`,
418
+ code: ErrorCode.PERMISSION_DENIED,
419
+ message: () => 'Permission denied',
422
420
  },
423
421
  ELOOP: {
424
- code: ErrorCode.E_SYMLINK_NOT_ALLOWED,
425
- message: (requestedPath) => `Too many symbolic links in path (possible circular reference): ${requestedPath}`,
422
+ code: ErrorCode.SYMLINK_NOT_ALLOWED,
423
+ message: () => 'Too many symbolic links (circular reference)',
426
424
  },
427
425
  ENAMETOOLONG: {
428
- code: ErrorCode.E_INVALID_INPUT,
429
- message: (requestedPath) => `Path name too long: ${requestedPath}`,
426
+ code: ErrorCode.INVALID_INPUT,
427
+ message: () => 'Path name too long',
430
428
  },
431
429
  };
432
430
  function buildAllowedDirectoriesHint() {
@@ -448,26 +446,28 @@ function toMcpError(requestedPath, error) {
448
446
  else if (typeof error === 'string') {
449
447
  originalMessage = error;
450
448
  }
451
- return new McpError(ErrorCode.E_NOT_FOUND, `Path is not accessible: ${requestedPath}`, requestedPath, { originalCode: code, originalMessage }, error);
449
+ return new McpError(ErrorCode.NOT_FOUND, 'Path is not accessible', requestedPath, { originalCode: code, originalMessage }, error);
452
450
  }
453
451
  function toAccessDeniedWithHint(requestedPath, resolvedPath, normalizedResolved) {
454
452
  const suggestion = buildAllowedDirectoriesHint();
455
- return new McpError(ErrorCode.E_ACCESS_DENIED, `Access denied: Path '${requestedPath}' is outside allowed directories.\n${suggestion}`, requestedPath, { resolvedPath, normalizedResolvedPath: normalizedResolved });
453
+ return new McpError(ErrorCode.ACCESS_DENIED, `Outside allowed directories. ${suggestion}`, requestedPath, { resolvedPath, normalizedResolvedPath: normalizedResolved });
456
454
  }
457
455
  function ensureWithinAllowedDirectories(options) {
458
456
  const { normalizedPath, requestedPath, allowedDirs, details } = options;
459
457
  if (isPathWithinDirectories(normalizedPath, allowedDirs))
460
458
  return;
461
459
  if (allowedDirs.length === 0) {
462
- throw new McpError(ErrorCode.E_ACCESS_DENIED, 'Access denied: No allowed directories configured. Use --allow-cwd or configure roots via the MCP Roots protocol.', requestedPath, details);
460
+ Logger.warn('Access denied: no allowed directories configured');
461
+ throw new McpError(ErrorCode.ACCESS_DENIED, 'No allowed directories configured. Use --allow-cwd or configure roots.', requestedPath, details);
463
462
  }
464
- throw new McpError(ErrorCode.E_ACCESS_DENIED, `Access denied: Path '${requestedPath}' is outside allowed directories`, requestedPath, details);
463
+ Logger.warn(`Access denied: path outside allowed directories (${requestedPath})`);
464
+ throw new McpError(ErrorCode.ACCESS_DENIED, 'Outside allowed directories', requestedPath, details);
465
465
  }
466
466
  async function resolveRealPathOrThrow(options) {
467
467
  const { requestedPath, normalizedRequested, signal } = options;
468
468
  try {
469
469
  assertNotAborted(signal);
470
- return await withAbort(fs.realpath(normalizedRequested), signal);
470
+ return await withAbort(realpath(normalizedRequested), signal);
471
471
  }
472
472
  catch (error) {
473
473
  rethrowIfAborted(error);
@@ -494,7 +494,7 @@ function ensureResolvedPathAllowed(options) {
494
494
  async function statPathOrThrow(requestedPath, resolvedPath, signal) {
495
495
  try {
496
496
  assertNotAborted(signal);
497
- return await withAbort(fs.stat(resolvedPath), signal);
497
+ return await withAbort(stat(resolvedPath), signal);
498
498
  }
499
499
  catch (error) {
500
500
  rethrowIfAborted(error);
@@ -507,7 +507,7 @@ async function resolveNearestExistingRealPathOrThrow(options) {
507
507
  for (;;) {
508
508
  try {
509
509
  assertNotAborted(signal);
510
- return await withAbort(fs.realpath(current), signal);
510
+ return await withAbort(realpath(current), signal);
511
511
  }
512
512
  catch (error) {
513
513
  rethrowIfAborted(error);
@@ -515,7 +515,7 @@ async function resolveNearestExistingRealPathOrThrow(options) {
515
515
  if (code !== 'ENOENT') {
516
516
  throw toMcpError(requestedPath, error);
517
517
  }
518
- const parent = path.dirname(current);
518
+ const parent = dirname(current);
519
519
  if (parent === current) {
520
520
  throw toMcpError(requestedPath, error);
521
521
  }
@@ -554,7 +554,7 @@ export async function validateExistingDirectory(requestedPath, signal) {
554
554
  const details = await validateExistingPathDetailsInternal(requestedPath, signal);
555
555
  const stats = await statPathOrThrow(requestedPath, details.resolvedPath, signal);
556
556
  if (!stats.isDirectory()) {
557
- throw new McpError(ErrorCode.E_NOT_DIRECTORY, `Not a directory: ${requestedPath}`, requestedPath);
557
+ throw new McpError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requestedPath);
558
558
  }
559
559
  return details.resolvedPath;
560
560
  }
@@ -581,7 +581,7 @@ function isFileRoot(root) {
581
581
  async function maybeAddRealPath(normalizedPath, validDirs, signal) {
582
582
  try {
583
583
  assertNotAborted(signal);
584
- const realPath = await withAbort(fs.realpath(normalizedPath), signal);
584
+ const realPath = await withAbort(realpath(normalizedPath), signal);
585
585
  const normalizedReal = normalizePath(realPath);
586
586
  if (!isSamePath(normalizedReal, normalizedPath)) {
587
587
  validDirs.push(normalizedReal);
@@ -596,7 +596,7 @@ async function resolveRootDirectory(root, signal) {
596
596
  const dirPath = fileURLToPath(root.uri);
597
597
  const normalizedPath = normalizePath(dirPath);
598
598
  assertNotAborted(signal);
599
- const stats = await withAbort(fs.stat(normalizedPath), signal);
599
+ const stats = await withAbort(stat(normalizedPath), signal);
600
600
  if (!stats.isDirectory())
601
601
  return null;
602
602
  return normalizedPath;
@@ -94,7 +94,7 @@ export function createInMemoryResourceStore(options = {}) {
94
94
  bytes: entryBytes,
95
95
  reason: 'entry_too_large',
96
96
  });
97
- throw new McpError(ErrorCode.E_TOO_LARGE, `Resource too large to cache (${entryBytes} bytes)`);
97
+ throw new McpError(ErrorCode.TOO_LARGE, `Resource too large to cache (${entryBytes} bytes).`);
98
98
  }
99
99
  const contentHash = computeSha256(params.text);
100
100
  const indexKey = buildIndexKey(mimeType, contentHash);
@@ -146,7 +146,7 @@ export function createInMemoryResourceStore(options = {}) {
146
146
  bytes: entry.size,
147
147
  reason: 'evicted_immediately',
148
148
  });
149
- throw new McpError(ErrorCode.E_TOO_LARGE, 'Resource cache full: entry evicted immediately');
149
+ throw new McpError(ErrorCode.TOO_LARGE, 'Cache full: entry evicted.');
150
150
  }
151
151
  return entry;
152
152
  }
@@ -158,7 +158,7 @@ export function createInMemoryResourceStore(options = {}) {
158
158
  uri,
159
159
  reason: 'not_found',
160
160
  });
161
- throw new McpError(ErrorCode.E_NOT_FOUND, `Resource not found: ${uri}. The cached result may have been evicted. Re-run the originating tool to regenerate.`);
161
+ throw new McpError(ErrorCode.NOT_FOUND, `Resource not found: ${uri}. Re-run the tool to regenerate.`);
162
162
  }
163
163
  if (isExpired(existing)) {
164
164
  removeEntry(uri, 'expired');
@@ -167,7 +167,7 @@ export function createInMemoryResourceStore(options = {}) {
167
167
  uri,
168
168
  reason: 'expired',
169
169
  });
170
- throw new McpError(ErrorCode.E_NOT_FOUND, `Resource expired: ${uri}. Re-run the originating tool to regenerate.`);
170
+ throw new McpError(ErrorCode.NOT_FOUND, `Resource expired: ${uri}. Re-run the tool to regenerate.`);
171
171
  }
172
172
  publishResourceStoreDiagnostics({
173
173
  phase: 'cache_hit',
@@ -5,15 +5,3 @@ export declare function debounce<Args extends unknown[]>(func: (...args: Args) =
5
5
  };
6
6
  export declare function mergeOptions<T extends object>(defaults: T, overrides: Partial<T>): T;
7
7
  export declare function omitOptionKeys<T extends object, K extends keyof T>(input: T, keys: readonly K[]): Omit<T, K>;
8
- interface ProgressPayload {
9
- current: number;
10
- total?: number;
11
- }
12
- type ProgressCallback = ((progress: ProgressPayload) => void) | undefined;
13
- interface PeriodicProgressOptions {
14
- total?: number;
15
- throttleModulo?: number;
16
- force?: boolean;
17
- }
18
- export declare function reportPeriodicProgress(onProgress: ProgressCallback, current: number, options?: PeriodicProgressOptions): void;
19
- export {};
package/dist/lib/utils.js CHANGED
@@ -35,16 +35,3 @@ export function omitOptionKeys(input, keys) {
35
35
  }
36
36
  return output;
37
37
  }
38
- export function reportPeriodicProgress(onProgress, current, options = {}) {
39
- if (!onProgress || current === 0)
40
- return;
41
- const throttleModulo = options.throttleModulo ?? 1;
42
- const force = options.force ?? false;
43
- if (!force && throttleModulo > 1 && current % throttleModulo !== 0) {
44
- return;
45
- }
46
- onProgress({
47
- current,
48
- ...(options.total !== undefined ? { total: options.total } : {}),
49
- });
50
- }