@j0hanz/filesystem-mcp 1.11.0 → 1.12.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.
Files changed (42) hide show
  1. package/dist/completions.js +25 -0
  2. package/dist/lib/constants.d.ts +2 -0
  3. package/dist/lib/constants.js +2 -0
  4. package/dist/lib/file-operations/metadata.js +71 -142
  5. package/dist/lib/file-operations/search.js +119 -19
  6. package/dist/lib/file-operations/traversal.js +45 -102
  7. package/dist/lib/fs-helpers.js +175 -158
  8. package/dist/prompts.d.ts +1 -0
  9. package/dist/prompts.js +58 -0
  10. package/dist/resources/generated-instructions.js +11 -3
  11. package/dist/resources/tool-catalog.js +49 -3
  12. package/dist/resources/tool-info.d.ts +1 -0
  13. package/dist/resources/tool-info.js +139 -8
  14. package/dist/resources.js +2 -0
  15. package/dist/schemas.d.ts +2 -0
  16. package/dist/schemas.js +10 -4
  17. package/dist/server/bootstrap.js +41 -5
  18. package/dist/tools/apply-patch.js +30 -33
  19. package/dist/tools/calculate-hash.js +21 -24
  20. package/dist/tools/contract.d.ts +1 -1
  21. package/dist/tools/create-directory.js +3 -3
  22. package/dist/tools/delete-file.js +2 -2
  23. package/dist/tools/diff-files.js +32 -20
  24. package/dist/tools/edit-file.d.ts +4 -1
  25. package/dist/tools/edit-file.js +172 -104
  26. package/dist/tools/list-directory.js +105 -10
  27. package/dist/tools/move-file.js +3 -3
  28. package/dist/tools/read-multiple.d.ts +1 -1
  29. package/dist/tools/read-multiple.js +123 -99
  30. package/dist/tools/read.js +104 -76
  31. package/dist/tools/replace-in-files.d.ts +5 -2
  32. package/dist/tools/replace-in-files.js +142 -76
  33. package/dist/tools/roots.js +1 -1
  34. package/dist/tools/search-content.js +249 -218
  35. package/dist/tools/shared.js +39 -7
  36. package/dist/tools/stat-many.js +12 -19
  37. package/dist/tools/stat.js +1 -1
  38. package/dist/tools/task-support.d.ts +7 -0
  39. package/dist/tools/task-support.js +140 -101
  40. package/dist/tools/write-file.js +4 -4
  41. package/dist/tools.js +2 -2
  42. package/package.json +1 -1
@@ -1,12 +1,15 @@
1
1
  import * as path from 'node:path';
2
- import { DEFAULT_READ_MANY_MAX_TOTAL_SIZE, DEFAULT_SEARCH_TIMEOUT_MS, } from '../lib/constants.js';
2
+ import { DEFAULT_SEARCH_TIMEOUT_MS } from '../lib/constants.js';
3
3
  import { ErrorCode } from '../lib/errors.js';
4
4
  import { readMultipleFiles } from '../lib/file-operations/metadata.js';
5
5
  import { ReadMultipleFilesInputSchema, ReadMultipleFilesOutputSchema, } from '../schemas.js';
6
6
  import { buildBatchCompletionSuffix, buildBatchPathContext, buildResourceLink, buildToolErrorResponse, buildToolResponse, createBatchProgressCallbacks, executeToolWithDiagnostics, maybeExternalizeTextContent, READ_ONLY_TOOL_ANNOTATIONS, resolveFinalProgressCurrent, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
7
7
  import { registerToolTaskIfAvailable } from './task-support.js';
8
- export const READ_MULTIPLE_FILES_TOOL = {
9
- name: 'read_many',
8
+ const READ_MANY_TOOL_NAME = 'read_many';
9
+ const READ_MANY_TOOL_LABEL = '🕮 read_many';
10
+ const FULL_FILE_CONTENTS_DESCRIPTION = 'Full file contents';
11
+ export const READ_MANY_TOOL = {
12
+ name: READ_MANY_TOOL_NAME,
10
13
  title: 'Read Multiple Files',
11
14
  description: 'Read multiple text files in one request with contents and metadata. ' +
12
15
  'For a single file, use `read`.',
@@ -19,131 +22,152 @@ export const READ_MULTIPLE_FILES_TOOL = {
19
22
  'Per-file `truncationReason` can be `head`, `tail`, `range`, or `externalized`.',
20
23
  ],
21
24
  };
25
+ function buildReadManyResourceName(filePath) {
26
+ return `read:${path.basename(filePath)}`;
27
+ }
22
28
  function toStructuredReadManyResult(result) {
23
- const structured = {
29
+ const structuredResult = {
24
30
  path: result.path,
25
31
  };
26
32
  if (result.content !== undefined)
27
- structured.content = result.content;
33
+ structuredResult.content = result.content;
28
34
  if (result.truncated)
29
- structured.truncated = result.truncated;
35
+ structuredResult.truncated = true;
30
36
  if (result.resourceUri)
31
- structured.resourceUri = result.resourceUri;
37
+ structuredResult.resourceUri = result.resourceUri;
32
38
  if (result.head !== undefined)
33
- structured.head = result.head;
39
+ structuredResult.head = result.head;
34
40
  if (result.tail !== undefined)
35
- structured.tail = result.tail;
36
- if (result.startLine !== undefined)
37
- structured.startLine = result.startLine;
41
+ structuredResult.tail = result.tail;
42
+ if (result.startLine !== undefined) {
43
+ structuredResult.startLine = result.startLine;
44
+ }
38
45
  if (result.endLine !== undefined)
39
- structured.endLine = result.endLine;
46
+ structuredResult.endLine = result.endLine;
40
47
  if (result.hasMoreLines)
41
- structured.hasMoreLines = result.hasMoreLines;
42
- if (result.totalLines !== undefined)
43
- structured.totalLines = result.totalLines;
44
- if (result.linesRead !== undefined)
45
- structured.linesRead = result.linesRead;
48
+ structuredResult.hasMoreLines = true;
49
+ if (result.totalLines !== undefined) {
50
+ structuredResult.totalLines = result.totalLines;
51
+ }
52
+ if (result.linesRead !== undefined) {
53
+ structuredResult.linesRead = result.linesRead;
54
+ }
46
55
  if (result.truncationReason) {
47
- structured.truncationReason = result.truncationReason;
56
+ structuredResult.truncationReason = result.truncationReason;
48
57
  }
49
58
  if (result.error)
50
- structured.error = result.error;
51
- return structured;
59
+ structuredResult.error = result.error;
60
+ return structuredResult;
52
61
  }
53
- async function handleReadMultipleFiles(args, signal, resourceStore, onReadComplete) {
54
- const options = {
55
- ...(signal ? { signal } : {}),
56
- ...(args.head !== undefined ? { head: args.head } : {}),
57
- ...(args.tail !== undefined ? { tail: args.tail } : {}),
58
- ...(args.startLine !== undefined ? { startLine: args.startLine } : {}),
59
- ...(args.endLine !== undefined ? { endLine: args.endLine } : {}),
60
- ...(onReadComplete ? { onReadComplete } : {}),
62
+ function resolveReadManyTruncationReason(result) {
63
+ if (!result.truncated)
64
+ return undefined;
65
+ if (result.readMode === 'head')
66
+ return 'head';
67
+ if (result.readMode === 'tail')
68
+ return 'tail';
69
+ if (result.readMode === 'range')
70
+ return 'range';
71
+ return undefined;
72
+ }
73
+ function maybeExternalizeReadManyResult(result, resourceStore) {
74
+ const truncationReason = resolveReadManyTruncationReason(result);
75
+ const baseResult = {
76
+ ...result,
77
+ ...(truncationReason ? { truncationReason } : {}),
61
78
  };
62
- const results = await readMultipleFiles(args.paths, options);
63
- const maxTotalSize = DEFAULT_READ_MANY_MAX_TOTAL_SIZE;
64
- const mappedResults = results.map((result) => {
65
- let baseTruncationReason;
66
- if (result.truncated && result.readMode === 'head') {
67
- baseTruncationReason = 'head';
68
- }
69
- else if (result.truncated && result.readMode === 'tail') {
70
- baseTruncationReason = 'tail';
71
- }
72
- else if (result.truncated && result.readMode === 'range') {
73
- baseTruncationReason = 'range';
74
- }
75
- const baseResult = {
76
- ...result,
77
- maxTotalSize,
78
- ...(baseTruncationReason
79
- ? { truncationReason: baseTruncationReason }
80
- : {}),
81
- };
82
- if (!result.content) {
83
- return baseResult;
84
- }
85
- const externalized = maybeExternalizeTextContent(resourceStore, result.content, { name: `read:${path.basename(result.path)}`, mimeType: 'text/plain' });
86
- if (!externalized) {
87
- return baseResult;
88
- }
89
- return {
90
- ...baseResult,
91
- content: externalized.preview,
92
- truncated: true,
93
- resourceUri: externalized.entry.uri,
94
- truncationReason: 'externalized',
95
- };
96
- });
79
+ if (!result.content) {
80
+ return baseResult;
81
+ }
82
+ const externalized = maybeExternalizeTextContent(resourceStore, result.content, { name: buildReadManyResourceName(result.path), mimeType: 'text/plain' });
83
+ if (!externalized) {
84
+ return baseResult;
85
+ }
86
+ return {
87
+ ...baseResult,
88
+ content: externalized.preview,
89
+ truncated: true,
90
+ resourceUri: externalized.entry.uri,
91
+ truncationReason: 'externalized',
92
+ };
93
+ }
94
+ function buildReadManyTextSection(result) {
95
+ const header = `=== ${result.path} ===`;
96
+ if (result.error) {
97
+ return `${header}\nError: ${result.error}`;
98
+ }
99
+ return `${header}\n${result.content ?? ''}`;
100
+ }
101
+ function buildReadMultipleOptions(args, signal, onReadComplete) {
102
+ const options = {};
103
+ if (signal)
104
+ options.signal = signal;
105
+ if (args.head !== undefined)
106
+ options.head = args.head;
107
+ if (args.tail !== undefined)
108
+ options.tail = args.tail;
109
+ if (args.startLine !== undefined)
110
+ options.startLine = args.startLine;
111
+ if (args.endLine !== undefined)
112
+ options.endLine = args.endLine;
113
+ if (onReadComplete)
114
+ options.onReadComplete = onReadComplete;
115
+ return options;
116
+ }
117
+ function buildReadManyResponsePayload(results, resourceStore) {
118
+ const resourceLinks = [];
119
+ const structuredResults = [];
120
+ const textSections = [];
97
121
  let succeeded = 0;
98
- let failed = 0;
99
- for (const result of mappedResults) {
100
- if (result.error === undefined)
122
+ for (const result of results) {
123
+ const mappedResult = maybeExternalizeReadManyResult(result, resourceStore);
124
+ structuredResults.push(toStructuredReadManyResult(mappedResult));
125
+ textSections.push(buildReadManyTextSection(mappedResult));
126
+ if (mappedResult.resourceUri) {
127
+ resourceLinks.push(buildResourceLink({
128
+ uri: mappedResult.resourceUri,
129
+ name: buildReadManyResourceName(mappedResult.path),
130
+ description: FULL_FILE_CONTENTS_DESCRIPTION,
131
+ }));
132
+ }
133
+ if (mappedResult.error === undefined)
101
134
  succeeded += 1;
102
- else
103
- failed += 1;
104
135
  }
105
- const structured = {
106
- ok: true,
107
- results: mappedResults.map((result) => toStructuredReadManyResult(result)),
136
+ const total = structuredResults.length;
137
+ return {
138
+ resourceLinks,
139
+ structuredResults,
108
140
  summary: {
109
- total: mappedResults.length,
141
+ total,
110
142
  succeeded,
111
- failed,
143
+ failed: total - succeeded,
112
144
  },
145
+ text: textSections.join('\n\n'),
113
146
  };
114
- const resourceLinks = [];
115
- for (const result of mappedResults) {
116
- if (!result.resourceUri)
117
- continue;
118
- resourceLinks.push(buildResourceLink({
119
- uri: result.resourceUri,
120
- name: `read:${path.basename(result.path)}`,
121
- description: 'Full file contents',
122
- }));
123
- }
124
- const text = mappedResults
125
- .map((result) => {
126
- const header = `=== ${result.path} ===`;
127
- if (result.error) {
128
- return `${header}\nError: ${result.error}`;
129
- }
130
- return `${header}\n${result.content ?? ''}`;
131
- })
132
- .join('\n\n');
133
- return buildToolResponse(text, structured, resourceLinks);
147
+ }
148
+ async function handleReadMultipleFiles(args, signal, resourceStore, onReadComplete) {
149
+ const options = buildReadMultipleOptions(args, signal, onReadComplete);
150
+ const results = await readMultipleFiles(args.paths, options);
151
+ const payload = buildReadManyResponsePayload(results, resourceStore);
152
+ const structured = {
153
+ ok: true,
154
+ results: payload.structuredResults,
155
+ summary: payload.summary,
156
+ };
157
+ return buildToolResponse(payload.text, structured, payload.resourceLinks);
134
158
  }
135
159
  export function registerReadMultipleFilesTool(server, options = {}) {
136
160
  const handler = (args, extra) => {
137
161
  const primaryPath = args.paths[0] ?? '';
138
162
  return executeToolWithDiagnostics({
139
- toolName: 'read_many',
163
+ toolName: READ_MANY_TOOL_NAME,
140
164
  extra,
141
165
  timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
142
166
  context: { path: primaryPath },
143
167
  run: async (signal) => {
144
168
  const context = buildBatchPathContext(args.paths, 'files');
145
169
  const { progress, onItemComplete } = createBatchProgressCallbacks(extra, {
146
- toolLabel: '🕮 read_many',
170
+ toolLabel: READ_MANY_TOOL_LABEL,
147
171
  context,
148
172
  totalItems: args.paths.length,
149
173
  itemVerb: 'read',
@@ -154,11 +178,11 @@ export function registerReadMultipleFilesTool(server, options = {}) {
154
178
  const suffix = buildBatchCompletionSuffix(sc.summary, 'files read', 'file read');
155
179
  const total = sc.summary?.total ?? 0;
156
180
  const finalCurrent = resolveFinalProgressCurrent(progress, total);
157
- progress.complete(`🕮 read_many: ${context} • ${suffix}`, finalCurrent);
181
+ progress.complete(`${READ_MANY_TOOL_LABEL}: ${context} • ${suffix}`, finalCurrent);
158
182
  return result;
159
183
  }
160
184
  catch (error) {
161
- progress.fail(`🕮 read_many: ${context} • failed`);
185
+ progress.fail(`${READ_MANY_TOOL_LABEL}: ${context} • failed`);
162
186
  throw error;
163
187
  }
164
188
  },
@@ -169,7 +193,7 @@ export function registerReadMultipleFilesTool(server, options = {}) {
169
193
  guard: options.isInitialized,
170
194
  });
171
195
  const validatedHandler = withValidatedArgs(ReadMultipleFilesInputSchema, wrappedHandler);
172
- if (registerToolTaskIfAvailable(server, 'read_many', READ_MULTIPLE_FILES_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
196
+ if (registerToolTaskIfAvailable(server, READ_MANY_TOOL_NAME, READ_MANY_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
173
197
  return;
174
- server.registerTool('read_many', withDefaultIcons({ ...READ_MULTIPLE_FILES_TOOL }, options.iconInfo), validatedHandler);
198
+ server.registerTool(READ_MANY_TOOL_NAME, withDefaultIcons({ ...READ_MANY_TOOL }, options.iconInfo), validatedHandler);
175
199
  }
@@ -18,53 +18,63 @@ export const READ_FILE_TOOL = {
18
18
  nuances: [
19
19
  'Large content is externalized to `filesystem-mcp://result/{id}` and preview is returned inline.',
20
20
  ],
21
- taskSupport: 'optional',
21
+ taskSupport: 'forbidden',
22
22
  };
23
- async function handleReadFile(args, signal, resourceStore) {
23
+ const READ_TOOL_NAME = 'read';
24
+ const READ_TOOL_LABEL = '🕮 read';
25
+ const FULL_FILE_CONTENTS_DESCRIPTION = 'Full file contents';
26
+ function buildReadResourceName(filePath) {
27
+ return `read:${path.basename(filePath)}`;
28
+ }
29
+ function buildReadOptions(args, signal) {
24
30
  const options = {
25
31
  encoding: 'utf-8',
26
32
  maxSize: MAX_TEXT_FILE_SIZE,
27
33
  skipBinary: true,
28
34
  };
29
- if (args.head !== undefined) {
35
+ if (signal)
36
+ options.signal = signal;
37
+ if (args.head !== undefined)
30
38
  options.head = args.head;
31
- }
32
- if (args.tail !== undefined) {
39
+ if (args.tail !== undefined)
33
40
  options.tail = args.tail;
34
- }
35
- if (args.startLine !== undefined) {
41
+ if (args.startLine !== undefined)
36
42
  options.startLine = args.startLine;
37
- }
38
- if (args.endLine !== undefined) {
43
+ if (args.endLine !== undefined)
39
44
  options.endLine = args.endLine;
40
- }
41
- if (signal) {
42
- options.signal = signal;
43
- }
44
- const result = await readFile(args.path, options);
45
+ return options;
46
+ }
47
+ function toStructuredReadFileResult(args, result) {
45
48
  const structured = {
46
49
  ok: true,
47
50
  path: args.path,
48
51
  content: result.content,
49
- ...(result.truncated ? { truncated: result.truncated } : {}),
50
- ...(result.totalLines !== undefined
51
- ? { totalLines: result.totalLines }
52
- : {}),
53
- ...(result.head !== undefined ? { head: result.head } : {}),
54
- ...(result.tail !== undefined ? { tail: result.tail } : {}),
55
- ...(result.startLine !== undefined ? { startLine: result.startLine } : {}),
56
- ...(result.endLine !== undefined ? { endLine: result.endLine } : {}),
57
- ...(result.linesRead !== undefined ? { linesRead: result.linesRead } : {}),
58
- ...(result.hasMoreLines ? { hasMoreLines: result.hasMoreLines } : {}),
59
52
  };
60
- const externalized = maybeExternalizeTextContent(resourceStore, result.content, { name: `read:${path.basename(args.path)}`, mimeType: 'text/plain' });
61
- if (args.includeHash) {
62
- structured.contentHash = createHash('sha256')
63
- .update(result.content, 'utf-8')
64
- .digest('hex');
65
- }
53
+ if (result.truncated)
54
+ structured.truncated = true;
55
+ if (result.totalLines !== undefined)
56
+ structured.totalLines = result.totalLines;
57
+ if (result.head !== undefined)
58
+ structured.head = result.head;
59
+ if (result.tail !== undefined)
60
+ structured.tail = result.tail;
61
+ if (result.startLine !== undefined)
62
+ structured.startLine = result.startLine;
63
+ if (result.endLine !== undefined)
64
+ structured.endLine = result.endLine;
65
+ if (result.linesRead !== undefined)
66
+ structured.linesRead = result.linesRead;
67
+ if (result.hasMoreLines)
68
+ structured.hasMoreLines = true;
69
+ return structured;
70
+ }
71
+ function maybeBuildExternalizedReadResponse(filePath, content, structured, resourceStore) {
72
+ const externalized = maybeExternalizeTextContent(resourceStore, content, {
73
+ name: buildReadResourceName(filePath),
74
+ mimeType: 'text/plain',
75
+ });
66
76
  if (!externalized) {
67
- return buildToolResponse(result.content, structured);
77
+ return undefined;
68
78
  }
69
79
  const { entry, preview } = externalized;
70
80
  const structuredWithResource = {
@@ -74,7 +84,7 @@ async function handleReadFile(args, signal, resourceStore) {
74
84
  resourceUri: entry.uri,
75
85
  };
76
86
  const text = [
77
- `Output too large to inline (${result.content.length} chars).`,
87
+ `Output too large to inline (${content.length} chars).`,
78
88
  'Preview:',
79
89
  preview,
80
90
  ].join('\n');
@@ -83,14 +93,70 @@ async function handleReadFile(args, signal, resourceStore) {
83
93
  uri: entry.uri,
84
94
  name: entry.name,
85
95
  mimeType: entry.mimeType,
86
- description: 'Full file contents',
96
+ description: FULL_FILE_CONTENTS_DESCRIPTION,
87
97
  expiresAt: entry.expiresAt,
88
98
  }),
89
99
  ]);
90
100
  }
101
+ function buildReadProgressMessage(args) {
102
+ const name = path.basename(args.path);
103
+ if (args.startLine !== undefined) {
104
+ const end = args.endLine ?? '…';
105
+ return `${READ_TOOL_LABEL}: ${name} [lines ${args.startLine}–${end}]`;
106
+ }
107
+ if (args.head !== undefined)
108
+ return `${READ_TOOL_LABEL}: ${name} [head ${args.head}]`;
109
+ if (args.tail !== undefined)
110
+ return `${READ_TOOL_LABEL}: ${name} [tail ${args.tail}]`;
111
+ return `${READ_TOOL_LABEL}: ${name}`;
112
+ }
113
+ function buildReadCompletionMessage(args, result) {
114
+ const name = path.basename(args.path);
115
+ if (result.isError)
116
+ return `${READ_TOOL_LABEL}: ${name} • failed`;
117
+ const structured = result.structuredContent;
118
+ if (!structured.ok)
119
+ return `${READ_TOOL_LABEL}: ${name} • failed`;
120
+ const lines = structured.linesRead ?? structured.totalLines;
121
+ if (structured.startLine !== undefined) {
122
+ const end = structured.linesRead
123
+ ? structured.startLine + structured.linesRead - 1
124
+ : (structured.endLine ?? '…');
125
+ return `${READ_TOOL_LABEL}: ${name} • lines ${structured.startLine}–${end}`;
126
+ }
127
+ if (structured.head !== undefined) {
128
+ return structured.hasMoreLines
129
+ ? `${READ_TOOL_LABEL}: ${name} • first ${String(lines ?? structured.head)} lines`
130
+ : `${READ_TOOL_LABEL}: ${name} • ${String(lines ?? structured.head)} lines`;
131
+ }
132
+ if (structured.tail !== undefined) {
133
+ return structured.hasMoreLines
134
+ ? `${READ_TOOL_LABEL}: ${name} • last ${String(lines ?? structured.tail)} lines`
135
+ : `${READ_TOOL_LABEL}: ${name} • ${String(lines ?? structured.tail)} lines`;
136
+ }
137
+ if (structured.truncated) {
138
+ return `${READ_TOOL_LABEL}: ${name} • truncated [${String(lines)} lines]`;
139
+ }
140
+ return `${READ_TOOL_LABEL}: ${name} • ${String(lines)} lines`;
141
+ }
142
+ async function handleReadFile(args, signal, resourceStore) {
143
+ const options = buildReadOptions(args, signal);
144
+ const result = await readFile(args.path, options);
145
+ const structured = toStructuredReadFileResult(args, result);
146
+ if (args.includeHash) {
147
+ structured.contentHash = createHash('sha256')
148
+ .update(result.content, 'utf-8')
149
+ .digest('hex');
150
+ }
151
+ const externalizedResponse = maybeBuildExternalizedReadResponse(args.path, result.content, structured, resourceStore);
152
+ if (externalizedResponse) {
153
+ return externalizedResponse;
154
+ }
155
+ return buildToolResponse(result.content, structured);
156
+ }
91
157
  export function registerReadFileTool(server, options = {}) {
92
158
  const handler = (args, extra) => executeToolWithDiagnostics({
93
- toolName: 'read',
159
+ toolName: READ_TOOL_NAME,
94
160
  extra,
95
161
  timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
96
162
  context: { path: args.path },
@@ -99,49 +165,11 @@ export function registerReadFileTool(server, options = {}) {
99
165
  });
100
166
  const wrappedHandler = wrapToolHandler(handler, {
101
167
  guard: options.isInitialized,
102
- progressMessage: (args) => {
103
- const name = path.basename(args.path);
104
- if (args.startLine !== undefined) {
105
- const end = args.endLine ?? '…';
106
- return `🕮 read: ${name} [lines ${args.startLine}–${end}]`;
107
- }
108
- if (args.head !== undefined)
109
- return `🕮 read: ${name} [head ${args.head}]`;
110
- if (args.tail !== undefined)
111
- return `🕮 read: ${name} [tail ${args.tail}]`;
112
- return `🕮 read: ${name}`;
113
- },
114
- completionMessage: (args, result) => {
115
- const name = path.basename(args.path);
116
- if (result.isError)
117
- return `🕮 read: ${name} • failed`;
118
- const sc = result.structuredContent;
119
- if (!sc.ok)
120
- return `🕮 read: ${name} • failed`;
121
- const lines = sc.linesRead ?? sc.totalLines;
122
- if (sc.startLine !== undefined) {
123
- const end = sc.linesRead
124
- ? sc.startLine + sc.linesRead - 1
125
- : (sc.endLine ?? '…');
126
- return `🕮 read: ${name} • lines ${sc.startLine}–${end}`;
127
- }
128
- if (sc.head !== undefined) {
129
- return sc.hasMoreLines
130
- ? `🕮 read: ${name} • first ${lines ?? sc.head} lines`
131
- : `🕮 read: ${name} • ${lines ?? sc.head} lines`;
132
- }
133
- if (sc.tail !== undefined) {
134
- return sc.hasMoreLines
135
- ? `🕮 read: ${name} • last ${lines ?? sc.tail} lines`
136
- : `🕮 read: ${name} • ${lines ?? sc.tail} lines`;
137
- }
138
- if (sc.truncated)
139
- return `🕮 read: ${name} • truncated [${String(lines)} lines]`;
140
- return `🕮 read: ${name} • ${String(lines)} lines`;
141
- },
168
+ progressMessage: buildReadProgressMessage,
169
+ completionMessage: buildReadCompletionMessage,
142
170
  });
143
171
  const validatedHandler = withValidatedArgs(ReadFileInputSchema, wrappedHandler);
144
- if (registerToolTaskIfAvailable(server, 'read', READ_FILE_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
172
+ if (registerToolTaskIfAvailable(server, READ_TOOL_NAME, READ_FILE_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
145
173
  return;
146
- server.registerTool('read', withDefaultIcons({ ...READ_FILE_TOOL }, options.iconInfo), validatedHandler);
174
+ server.registerTool(READ_TOOL_NAME, withDefaultIcons({ ...READ_FILE_TOOL }, options.iconInfo), validatedHandler);
147
175
  }
@@ -3,8 +3,11 @@ import type { z } from 'zod';
3
3
  import { SearchAndReplaceInputSchema, SearchAndReplaceOutputSchema } from '../schemas.js';
4
4
  import { type ToolContract, type ToolRegistrationOptions, type ToolResponse } from './shared.js';
5
5
  export declare const SEARCH_AND_REPLACE_TOOL: ToolContract;
6
- export declare function handleSearchAndReplace(args: z.infer<typeof SearchAndReplaceInputSchema>, signal?: AbortSignal, onProgress?: (progress: {
6
+ type SearchAndReplaceArgs = z.infer<typeof SearchAndReplaceInputSchema>;
7
+ type SearchAndReplaceOutput = z.infer<typeof SearchAndReplaceOutputSchema>;
8
+ export declare function handleSearchAndReplace(args: SearchAndReplaceArgs, signal?: AbortSignal, onProgress?: (progress: {
7
9
  total?: number;
8
10
  current: number;
9
- }) => void): Promise<ToolResponse<z.infer<typeof SearchAndReplaceOutputSchema>>>;
11
+ }) => void): Promise<ToolResponse<SearchAndReplaceOutput>>;
10
12
  export declare function registerSearchAndReplaceTool(server: McpServer, options?: ToolRegistrationOptions): void;
13
+ export {};