@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
@@ -1,4 +1,4 @@
1
- import * as path from 'node:path';
1
+ import { basename, relative } from 'node:path';
2
2
  import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_SEARCH_TIMEOUT_MS, } from '../lib/constants.js';
3
3
  import { ErrorCode } from '../lib/errors.js';
4
4
  import { searchFiles } from '../lib/file-operations/search.js';
@@ -17,6 +17,42 @@ export const SEARCH_FILES_TOOL = {
17
17
  nuances: ['Respects `.gitignore` unless `includeIgnored=true`.'],
18
18
  taskSupport: 'optional',
19
19
  };
20
+ function buildTruncatedReason(summary) {
21
+ if (!summary.truncated)
22
+ return undefined;
23
+ if (summary.stoppedReason === 'timeout')
24
+ return 'timeout';
25
+ if (summary.stoppedReason === 'maxFiles')
26
+ return `max files (${summary.filesScanned})`;
27
+ return `max results (${summary.matched})`;
28
+ }
29
+ function buildRelativeResults(basePath, displayResults) {
30
+ const relativeResults = [];
31
+ for (const entry of displayResults) {
32
+ relativeResults.push({
33
+ path: relative(basePath, entry.path),
34
+ size: entry.size,
35
+ modified: entry.modified?.toISOString(),
36
+ });
37
+ }
38
+ return relativeResults;
39
+ }
40
+ function computeNextCursor(summary, displayResultsCount, cursorOffset) {
41
+ if (summary.truncated && displayResultsCount > 0) {
42
+ return encodeOffsetCursor(cursorOffset + displayResultsCount);
43
+ }
44
+ return undefined;
45
+ }
46
+ function applySummaryFields(structured, summary, nextCursor) {
47
+ if (summary.truncated)
48
+ structured.truncated = true;
49
+ if (summary.skippedInaccessible)
50
+ structured.skippedInaccessible = summary.skippedInaccessible;
51
+ if (summary.stoppedReason)
52
+ structured.stoppedReason = summary.stoppedReason;
53
+ if (nextCursor !== undefined)
54
+ structured.nextCursor = nextCursor;
55
+ }
20
56
  async function handleSearchFiles(args, signal, onProgress) {
21
57
  const basePath = resolvePathOrRoot(args.path);
22
58
  const excludePatterns = args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS;
@@ -28,57 +64,34 @@ async function handleSearchFiles(args, signal, onProgress) {
28
64
  includeHidden: args.includeHidden,
29
65
  sortBy: args.sortBy,
30
66
  respectGitignore: !args.includeIgnored,
31
- ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),
32
- ...(onProgress ? { onProgress } : {}),
33
- ...(signal ? { signal } : {}),
34
67
  };
68
+ if (args.maxDepth !== undefined)
69
+ searchOptions.maxDepth = args.maxDepth;
70
+ if (onProgress)
71
+ searchOptions.onProgress = onProgress;
72
+ if (signal)
73
+ searchOptions.signal = signal;
35
74
  const result = await searchFiles(basePath, args.pattern, excludePatterns, searchOptions);
36
75
  const allResults = result.results;
37
- const displayResults = cursorOffset > 0 ? allResults.slice(cursorOffset) : allResults;
38
- const nextCursor = result.summary.truncated && displayResults.length > 0
39
- ? encodeOffsetCursor(cursorOffset + displayResults.length)
40
- : undefined;
41
- const relativeResults = [];
42
- for (const entry of displayResults) {
43
- relativeResults.push({
44
- path: path.relative(result.basePath, entry.path),
45
- size: entry.size,
46
- modified: entry.modified?.toISOString(),
47
- });
48
- }
76
+ let displayResults = allResults;
77
+ if (cursorOffset > 0)
78
+ displayResults = allResults.slice(cursorOffset);
79
+ const nextCursor = computeNextCursor(result.summary, displayResults.length, cursorOffset);
80
+ const relativeResults = buildRelativeResults(result.basePath, displayResults);
49
81
  const structured = {
50
82
  ok: true,
51
83
  root: basePath,
52
84
  results: relativeResults,
53
85
  totalMatches: result.summary.matched,
54
86
  filesScanned: result.summary.filesScanned,
55
- ...(result.summary.truncated
56
- ? { truncated: result.summary.truncated }
57
- : {}),
58
- ...(result.summary.skippedInaccessible
59
- ? { skippedInaccessible: result.summary.skippedInaccessible }
60
- : {}),
61
- ...(result.summary.stoppedReason
62
- ? { stoppedReason: result.summary.stoppedReason }
63
- : {}),
64
- ...(nextCursor !== undefined ? { nextCursor } : {}),
65
87
  };
66
- let truncatedReason;
67
- if (result.summary.truncated) {
68
- if (result.summary.stoppedReason === 'timeout') {
69
- truncatedReason = 'timeout';
70
- }
71
- else if (result.summary.stoppedReason === 'maxFiles') {
72
- truncatedReason = `max files (${result.summary.filesScanned})`;
73
- }
74
- else {
75
- truncatedReason = `max results (${result.summary.matched})`;
76
- }
77
- }
88
+ applySummaryFields(structured, result.summary, nextCursor);
89
+ const truncatedReason = buildTruncatedReason(result.summary);
78
90
  const summaryOptions = {
79
91
  truncated: result.summary.truncated,
80
- ...(truncatedReason ? { truncatedReason } : {}),
81
92
  };
93
+ if (truncatedReason)
94
+ summaryOptions.truncatedReason = truncatedReason;
82
95
  const textLines = [];
83
96
  if (relativeResults.length === 0) {
84
97
  textLines.push('No matches');
@@ -103,7 +116,7 @@ export function registerSearchFilesTool(server, options = {}) {
103
116
  timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
104
117
  context: { path: args.path ?? '.' },
105
118
  run: async (signal) => {
106
- const rawScopeLabel = args.path ? path.basename(args.path) : '.';
119
+ const rawScopeLabel = args.path ? basename(args.path) : '.';
107
120
  const scopeLabel = rawScopeLabel || '.';
108
121
  const { pattern } = args;
109
122
  const truncatedPattern = truncateProgressPattern(pattern);
@@ -161,7 +174,7 @@ export function registerSearchFilesTool(server, options = {}) {
161
174
  throw error;
162
175
  }
163
176
  },
164
- onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path),
177
+ onError: (error) => buildToolErrorResponse(error, ErrorCode.UNKNOWN, args.path),
165
178
  });
166
179
  const { isInitialized } = options;
167
180
  const wrappedHandler = wrapToolHandler(handler, {
@@ -63,6 +63,9 @@ export interface ToolExtra {
63
63
  signal?: AbortSignal;
64
64
  _meta?: {
65
65
  progressToken?: ProgressToken | undefined;
66
+ traceparent?: string | undefined;
67
+ tracestate?: string | undefined;
68
+ baggage?: string | undefined;
66
69
  };
67
70
  sendNotification?: (notification: {
68
71
  method: 'notifications/progress';
@@ -1,10 +1,11 @@
1
- import * as path from 'node:path';
2
1
  import { channel } from 'node:diagnostics_channel';
2
+ import { basename } from 'node:path';
3
3
  import { z } from 'zod';
4
+ import { createTimedAbortSignal } from '../lib/abort.js';
4
5
  import { parseTrueEnvFlag } from '../lib/constants.js';
5
6
  import { createDetailedError, ErrorCode, formatDetailedError, getSuggestion, McpError, } from '../lib/errors.js';
6
- import { createTimedAbortSignal } from '../lib/fs-helpers.js';
7
- import { withToolDiagnostics } from '../lib/observability.js';
7
+ import { Logger } from '../lib/logger.js';
8
+ import { withToolDiagnostics, } from '../lib/observability.js';
8
9
  import { getAllowedDirectories } from '../lib/paths.js';
9
10
  import { createBase64JsonCodec } from '../lib/zod-codecs.js';
10
11
  export {} from './contract.js';
@@ -12,6 +13,20 @@ const MAX_INLINE_CONTENT_CHARS = parseInt(process.env['FS_CONTEXT_MAX_INLINE_CHA
12
13
  const MAX_INLINE_PREVIEW_CHARS = 4_000;
13
14
  const PROGRESS_RATE_LIMIT_MS = 50;
14
15
  const CONTEXT_DIAGNOSTICS_CHANNEL = channel('filesystem-mcp:context');
16
+ // W3C Trace Context: version-traceid-parentid-traceflags
17
+ const TRACEPARENT_RE = /^[\da-f]{2}-[\da-f]{32}-[\da-f]{16}-[\da-f]{2}$/i;
18
+ function extractTraceContext(meta) {
19
+ const tp = meta?.traceparent;
20
+ if (typeof tp !== 'string' || !TRACEPARENT_RE.test(tp))
21
+ return undefined;
22
+ return {
23
+ traceparent: tp,
24
+ ...(typeof meta?.tracestate === 'string'
25
+ ? { tracestate: meta.tracestate }
26
+ : {}),
27
+ ...(typeof meta?.baggage === 'string' ? { baggage: meta.baggage } : {}),
28
+ };
29
+ }
15
30
  function publishContextDiagnostics(event) {
16
31
  if (!CONTEXT_DIAGNOSTICS_CHANNEL.hasSubscribers)
17
32
  return;
@@ -45,11 +60,13 @@ function normalizeToolExecution(tool) {
45
60
  ? candidate['execution']
46
61
  : undefined;
47
62
  const executionTaskSupport = existingExecution?.['taskSupport'];
48
- const resolvedTaskSupport = isTaskSupportLevel(topLevelTaskSupport)
49
- ? topLevelTaskSupport
50
- : isTaskSupportLevel(executionTaskSupport)
51
- ? executionTaskSupport
52
- : undefined;
63
+ let resolvedTaskSupport;
64
+ if (isTaskSupportLevel(topLevelTaskSupport)) {
65
+ resolvedTaskSupport = topLevelTaskSupport;
66
+ }
67
+ else if (isTaskSupportLevel(executionTaskSupport)) {
68
+ resolvedTaskSupport = executionTaskSupport;
69
+ }
53
70
  if (resolvedTaskSupport === undefined && topLevelTaskSupport === undefined) {
54
71
  return tool;
55
72
  }
@@ -106,6 +123,7 @@ export function maybeExternalizeTextContent(resourceStore, content, params) {
106
123
  chars: content.length,
107
124
  uri: entry.uri,
108
125
  });
126
+ Logger.debug(`Content externalized: ${params.name} (${content.length} chars) → ${entry.uri}`);
109
127
  return {
110
128
  entry,
111
129
  preview: buildTextPreview(content),
@@ -128,9 +146,12 @@ export function buildResourceLink(params) {
128
146
  }
129
147
  function resolveDetailedError(error, defaultCode, path) {
130
148
  const detailed = createDetailedError(error, path);
131
- if (detailed.code === ErrorCode.E_UNKNOWN) {
149
+ if (detailed.code === ErrorCode.UNKNOWN) {
132
150
  detailed.code = defaultCode;
133
- detailed.suggestion = getSuggestion(defaultCode);
151
+ const suggestion = getSuggestion(defaultCode);
152
+ if (suggestion) {
153
+ detailed.suggestion = suggestion;
154
+ }
134
155
  }
135
156
  return detailed;
136
157
  }
@@ -156,13 +177,13 @@ function validateStructuredContent(toolName, outputSchema, structuredContent) {
156
177
  if (parsed.success) {
157
178
  return parsed.data;
158
179
  }
159
- throw new McpError(ErrorCode.E_UNKNOWN, `Tool "${toolName}" returned invalid structuredContent.`, undefined, { errors: z.treeifyError(parsed.error) });
180
+ throw new McpError(ErrorCode.UNKNOWN, `Tool "${toolName}" returned invalid structuredContent.`, undefined, { errors: z.treeifyError(parsed.error) });
160
181
  }
161
182
  function validateToolResponse(toolName, result, outputSchema) {
162
183
  if (!outputSchema)
163
184
  return result;
164
185
  if (!Object.hasOwn(result, 'structuredContent')) {
165
- throw new McpError(ErrorCode.E_UNKNOWN, `Tool "${toolName}" returned success without structuredContent.`);
186
+ throw new McpError(ErrorCode.UNKNOWN, `Tool "${toolName}" returned success without structuredContent.`);
166
187
  }
167
188
  return {
168
189
  ...result,
@@ -175,12 +196,20 @@ function parseToolArgs(schema, args) {
175
196
  if (parsed.success) {
176
197
  return parsed.data;
177
198
  }
178
- throw new McpError(ErrorCode.E_INVALID_INPUT, `Invalid tool arguments:\n${z.prettifyError(parsed.error)}`, undefined, { errors: z.treeifyError(parsed.error) });
199
+ throw new McpError(ErrorCode.INVALID_INPUT, `Invalid tool arguments:\n${z.prettifyError(parsed.error)}`);
179
200
  }
180
201
  export function withValidatedArgs(schema, handler) {
181
202
  return async (args, extra) => {
182
- const normalizedArgs = parseToolArgs(schema, args);
183
- return handler(normalizedArgs, extra);
203
+ try {
204
+ const normalizedArgs = parseToolArgs(schema, args);
205
+ return await handler(normalizedArgs, extra);
206
+ }
207
+ catch (error) {
208
+ if (error instanceof McpError && error.code === ErrorCode.INVALID_INPUT) {
209
+ return buildToolErrorResponse(error, ErrorCode.INVALID_INPUT);
210
+ }
211
+ throw error;
212
+ }
184
213
  };
185
214
  }
186
215
  function canSendProgress(extra) {
@@ -232,7 +261,7 @@ export function buildFileInfoPayload(info) {
232
261
  : {}),
233
262
  };
234
263
  }
235
- const NOT_INITIALIZED_ERROR = new McpError(ErrorCode.E_INVALID_INPUT, 'Client not initialized; wait for notifications/initialized');
264
+ const NOT_INITIALIZED_ERROR = new McpError(ErrorCode.INVALID_INPUT, 'Client not initialized; wait for notifications/initialized');
236
265
  async function withToolErrorHandling(run, onError) {
237
266
  try {
238
267
  return await run();
@@ -249,6 +278,7 @@ function getToolSignal(extraSignal, timedSignal) {
249
278
  return { signal, cleanup };
250
279
  }
251
280
  export async function executeToolWithDiagnostics(options) {
281
+ const traceContext = extractTraceContext(options.extra._meta);
252
282
  return withToolDiagnostics(options.toolName, () => withToolErrorHandling(async () => {
253
283
  const { signal, cleanup } = getToolSignal(options.extra.signal, options.timedSignal);
254
284
  try {
@@ -257,7 +287,10 @@ export async function executeToolWithDiagnostics(options) {
257
287
  finally {
258
288
  cleanup();
259
289
  }
260
- }, options.onError), options.context);
290
+ }, options.onError), {
291
+ ...options.context,
292
+ ...(traceContext ? { traceContext } : {}),
293
+ });
261
294
  }
262
295
  export function buildToolErrorResponse(error, defaultCode, path) {
263
296
  const detailed = resolveDetailedError(error, defaultCode, path);
@@ -269,7 +302,7 @@ export function buildToolErrorResponse(error, defaultCode, path) {
269
302
  };
270
303
  }
271
304
  function buildNotInitializedResult() {
272
- return buildToolErrorResponse(NOT_INITIALIZED_ERROR, ErrorCode.E_INVALID_INPUT);
305
+ return buildToolErrorResponse(NOT_INITIALIZED_ERROR, ErrorCode.INVALID_INPUT);
273
306
  }
274
307
  async function reportProgress(extra, progress) {
275
308
  await updateTaskStoreProgress(extra, progress);
@@ -283,6 +316,10 @@ function formatTaskStatusMessage(progress) {
283
316
  }
284
317
  return progress.message ?? `${progress.current}`;
285
318
  }
319
+ function isBenignTaskStatusUpdateError(error) {
320
+ return (error instanceof Error &&
321
+ /Task .*not found|terminal status/iu.test(error.message));
322
+ }
286
323
  async function updateTaskStoreProgress(extra, progress) {
287
324
  const taskExtra = extra;
288
325
  if (typeof taskExtra.taskId === 'string' &&
@@ -294,7 +331,9 @@ async function updateTaskStoreProgress(extra, progress) {
294
331
  await store.updateTaskStatus(taskExtra.taskId, 'working', formatTaskStatusMessage(progress));
295
332
  }
296
333
  catch (error) {
297
- console.error('Failed to update task status message:', error);
334
+ if (isBenignTaskStatusUpdateError(error))
335
+ return;
336
+ Logger.error('Failed to update task status message:', error);
298
337
  }
299
338
  }
300
339
  }
@@ -315,7 +354,7 @@ async function sendMcpProgressNotification(extra, progress) {
315
354
  });
316
355
  }
317
356
  catch (error) {
318
- console.error('Failed to send progress notification:', error);
357
+ Logger.error('Failed to send progress notification:', error);
319
358
  }
320
359
  }
321
360
  }
@@ -480,14 +519,14 @@ export function resolvePathOrRoot(pathValue) {
480
519
  return pathValue;
481
520
  const roots = getAllowedDirectories();
482
521
  if (roots.length === 0) {
483
- throw new McpError(ErrorCode.E_ACCESS_DENIED, 'No workspace roots configured. Use the roots tool to check, or configure roots via the MCP Roots protocol (or start with --allow-cwd / CLI directories).');
522
+ throw new McpError(ErrorCode.ACCESS_DENIED, 'No roots configured. Use roots tool, --allow-cwd, or MCP Roots protocol.');
484
523
  }
485
524
  if (roots.length > 1) {
486
- throw new McpError(ErrorCode.E_INVALID_INPUT, 'Multiple workspace roots configured. Provide an explicit path to disambiguate.');
525
+ throw new McpError(ErrorCode.INVALID_INPUT, 'Multiple roots configured. Provide an explicit path.');
487
526
  }
488
527
  const root = roots[0];
489
528
  if (!root) {
490
- throw new McpError(ErrorCode.E_ACCESS_DENIED, 'Workspace root is unexpectedly undefined');
529
+ throw new McpError(ErrorCode.ACCESS_DENIED, 'Workspace root is unexpectedly undefined');
491
530
  }
492
531
  return root;
493
532
  }
@@ -505,14 +544,17 @@ export function decodeOffsetCursor(cursor) {
505
544
  catch {
506
545
  // fall through to throw
507
546
  }
508
- throw new McpError(ErrorCode.E_INVALID_INPUT, `Invalid cursor: the cursor value is malformed or corrupted. Request the first page without a cursor.`);
547
+ throw new McpError(ErrorCode.INVALID_INPUT, `Invalid cursor. Request the first page without a cursor.`);
509
548
  }
510
549
  export function buildBatchPathContext(paths, unitLabel = 'paths') {
511
550
  const normalizedLabel = paths.length === 1 ? unitLabel.replace(/s$/i, '') : unitLabel;
512
- const first = path.basename(paths[0] ?? '');
513
- const extraPaths = paths.length > 1
514
- ? `, ${path.basename(paths[1] ?? '')}${paths.length > 2 ? '…' : ''}`
515
- : '';
551
+ const first = basename(paths[0] ?? '');
552
+ let extraPaths = '';
553
+ if (paths.length > 1) {
554
+ const secondPath = basename(paths[1] ?? '');
555
+ const ellipsis = paths.length > 2 ? '…' : '';
556
+ extraPaths = `, ${secondPath}${ellipsis}`;
557
+ }
516
558
  return `${paths.length} ${normalizedLabel} [${first}${extraPaths}]`;
517
559
  }
518
560
  export function truncateProgressPattern(pattern, maxLength = 40) {
@@ -38,15 +38,19 @@ async function handleGetMultipleFileInfo(args, signal, onProgress) {
38
38
  path: entry.path,
39
39
  info: entry.info ? buildFileInfoPayload(entry.info) : undefined,
40
40
  error: entry.error
41
- ? buildStructuredError(entry.error, ErrorCode.E_NOT_FOUND, entry.path)
41
+ ? buildStructuredError(entry.error, ErrorCode.NOT_FOUND, entry.path)
42
42
  : undefined,
43
43
  }));
44
44
  const text = result.results
45
- .map((entry) => entry.error
46
- ? `${entry.path}: ${buildStructuredError(entry.error, ErrorCode.E_NOT_FOUND, entry.path).message}`
47
- : entry.info
48
- ? formatFileInfoDetail(entry.info)
49
- : entry.path)
45
+ .map((entry) => {
46
+ if (entry.error) {
47
+ return `${entry.path}: ${buildStructuredError(entry.error, ErrorCode.NOT_FOUND, entry.path).message}`;
48
+ }
49
+ if (entry.info) {
50
+ return formatFileInfoDetail(entry.info);
51
+ }
52
+ return entry.path;
53
+ })
50
54
  .join('\n\n');
51
55
  const structured = {
52
56
  ok: true,
@@ -90,7 +94,7 @@ export function registerGetMultipleFileInfoTool(server, options = {}) {
90
94
  throw error;
91
95
  }
92
96
  },
93
- onError: (error) => buildToolErrorResponse(error, ErrorCode.E_NOT_FOUND, primaryPath),
97
+ onError: (error) => buildToolErrorResponse(error, ErrorCode.NOT_FOUND, primaryPath),
94
98
  });
95
99
  };
96
100
  const wrappedHandler = wrapToolHandler(handler, {
@@ -1,4 +1,4 @@
1
- import * as path from 'node:path';
1
+ import { basename } from 'node:path';
2
2
  import { DEFAULT_SEARCH_TIMEOUT_MS } from '../lib/constants.js';
3
3
  import { ErrorCode } from '../lib/errors.js';
4
4
  import { getFileInfo } from '../lib/file-operations/metadata.js';
@@ -48,13 +48,13 @@ export function registerGetFileInfoTool(server, options = {}) {
48
48
  timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
49
49
  context: { path: args.path },
50
50
  run: (signal) => handleGetFileInfo(args, signal),
51
- onError: (error) => buildToolErrorResponse(error, ErrorCode.E_NOT_FOUND, args.path),
51
+ onError: (error) => buildToolErrorResponse(error, ErrorCode.NOT_FOUND, args.path),
52
52
  });
53
53
  const wrappedHandler = wrapToolHandler(handler, {
54
54
  guard: options.isInitialized,
55
- progressMessage: (args) => `🕮 stat: ${path.basename(args.path)}`,
55
+ progressMessage: (args) => `🕮 stat: ${basename(args.path)}`,
56
56
  completionMessage: (args, result) => {
57
- const name = path.basename(args.path);
57
+ const name = basename(args.path);
58
58
  if (result.isError)
59
59
  return `🕮 stat: ${name} • failed`;
60
60
  const sc = result.structuredContent;
@@ -2,20 +2,21 @@ import type { ToolTaskHandler } from '@modelcontextprotocol/sdk/experimental/tas
2
2
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import type { AnySchema, SchemaOutput, ShapeOutput, ZodRawShapeCompat } from '@modelcontextprotocol/sdk/server/zod-compat.js';
4
4
  import type { RequestTaskStore } from '@modelcontextprotocol/sdk/shared/protocol.js';
5
- import type { IconInfo, ToolExtra, ToolResult } from './shared.js';
5
+ import { type IconInfo, type ToolExtra, type ToolResult } from './shared.js';
6
6
  type TaskToolExtra = ToolExtra & {
7
7
  taskId?: string;
8
8
  taskStore?: RequestTaskStore;
9
9
  taskRequestedTtl?: number | null;
10
10
  };
11
- type ToolArgs<Args extends ZodRawShapeCompat | AnySchema | undefined> = Args extends ZodRawShapeCompat ? ShapeOutput<Args> : Args extends AnySchema ? SchemaOutput<Args> : undefined;
12
- export declare function registerToolTaskIfAvailable<Args extends ZodRawShapeCompat | AnySchema | undefined, Result>(server: McpServer, toolName: string, toolDef: object, run: (args: ToolArgs<Args>, extra: TaskToolExtra) => Promise<ToolResult<Result>>, iconInfo: IconInfo | undefined, guard?: () => boolean): boolean;
13
- export declare function createToolTaskHandler<Result>(run: (args: undefined, extra: TaskToolExtra) => Promise<ToolResult<Result>>, options?: {
11
+ export type ToolSchema = ZodRawShapeCompat | AnySchema | undefined;
12
+ type ToolArgs<Args extends ToolSchema> = Args extends ZodRawShapeCompat ? ShapeOutput<Args> : Args extends AnySchema ? SchemaOutput<Args> : undefined;
13
+ export declare function registerToolTaskIfAvailable<Args extends ToolSchema, Result>(server: McpServer, toolName: string, toolDef: object, run: (args: ToolArgs<Args>, extra: TaskToolExtra) => Promise<ToolResult<Result>>, iconInfo: IconInfo | undefined, guard?: () => boolean): boolean;
14
+ interface TaskHandlerOptions {
14
15
  guard?: () => boolean;
15
16
  toolName?: string;
16
- }): ToolTaskHandler;
17
- export declare function createToolTaskHandler<Args extends ZodRawShapeCompat | AnySchema, Result>(run: (args: ToolArgs<Args>, extra: TaskToolExtra) => Promise<ToolResult<Result>>, options?: {
18
- guard?: () => boolean;
19
- toolName?: string;
20
- }): ToolTaskHandler<Args>;
17
+ cancelPollMs?: number;
18
+ pollIntervalMs?: number;
19
+ }
20
+ export declare function createToolTaskHandler<Result>(run: (args: undefined, extra: TaskToolExtra) => Promise<ToolResult<Result>>, options?: TaskHandlerOptions): ToolTaskHandler;
21
+ export declare function createToolTaskHandler<Args extends ZodRawShapeCompat | AnySchema, Result>(run: (args: ToolArgs<Args>, extra: TaskToolExtra) => Promise<ToolResult<Result>>, options?: TaskHandlerOptions): ToolTaskHandler<Args>;
21
22
  export {};