@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.
- package/dist/completions.js +25 -0
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.js +2 -0
- package/dist/lib/file-operations/metadata.js +71 -142
- package/dist/lib/file-operations/search.js +119 -19
- package/dist/lib/file-operations/traversal.js +45 -102
- package/dist/lib/fs-helpers.js +175 -158
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.js +58 -0
- package/dist/resources/generated-instructions.js +11 -3
- package/dist/resources/tool-catalog.js +49 -3
- package/dist/resources/tool-info.d.ts +1 -0
- package/dist/resources/tool-info.js +139 -8
- package/dist/resources.js +2 -0
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.js +10 -4
- package/dist/server/bootstrap.js +41 -5
- package/dist/tools/apply-patch.js +30 -33
- package/dist/tools/calculate-hash.js +21 -24
- package/dist/tools/contract.d.ts +1 -1
- package/dist/tools/create-directory.js +3 -3
- package/dist/tools/delete-file.js +2 -2
- package/dist/tools/diff-files.js +32 -20
- package/dist/tools/edit-file.d.ts +4 -1
- package/dist/tools/edit-file.js +172 -104
- package/dist/tools/list-directory.js +105 -10
- package/dist/tools/move-file.js +3 -3
- package/dist/tools/read-multiple.d.ts +1 -1
- package/dist/tools/read-multiple.js +123 -99
- package/dist/tools/read.js +104 -76
- package/dist/tools/replace-in-files.d.ts +5 -2
- package/dist/tools/replace-in-files.js +142 -76
- package/dist/tools/roots.js +1 -1
- package/dist/tools/search-content.js +249 -218
- package/dist/tools/shared.js +39 -7
- package/dist/tools/stat-many.js +12 -19
- package/dist/tools/stat.js +1 -1
- package/dist/tools/task-support.d.ts +7 -0
- package/dist/tools/task-support.js +140 -101
- package/dist/tools/write-file.js +4 -4
- package/dist/tools.js +2 -2
- package/package.json +1 -1
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
|
-
import {
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
29
|
+
const structuredResult = {
|
|
24
30
|
path: result.path,
|
|
25
31
|
};
|
|
26
32
|
if (result.content !== undefined)
|
|
27
|
-
|
|
33
|
+
structuredResult.content = result.content;
|
|
28
34
|
if (result.truncated)
|
|
29
|
-
|
|
35
|
+
structuredResult.truncated = true;
|
|
30
36
|
if (result.resourceUri)
|
|
31
|
-
|
|
37
|
+
structuredResult.resourceUri = result.resourceUri;
|
|
32
38
|
if (result.head !== undefined)
|
|
33
|
-
|
|
39
|
+
structuredResult.head = result.head;
|
|
34
40
|
if (result.tail !== undefined)
|
|
35
|
-
|
|
36
|
-
if (result.startLine !== undefined)
|
|
37
|
-
|
|
41
|
+
structuredResult.tail = result.tail;
|
|
42
|
+
if (result.startLine !== undefined) {
|
|
43
|
+
structuredResult.startLine = result.startLine;
|
|
44
|
+
}
|
|
38
45
|
if (result.endLine !== undefined)
|
|
39
|
-
|
|
46
|
+
structuredResult.endLine = result.endLine;
|
|
40
47
|
if (result.hasMoreLines)
|
|
41
|
-
|
|
42
|
-
if (result.totalLines !== undefined)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
56
|
+
structuredResult.truncationReason = result.truncationReason;
|
|
48
57
|
}
|
|
49
58
|
if (result.error)
|
|
50
|
-
|
|
51
|
-
return
|
|
59
|
+
structuredResult.error = result.error;
|
|
60
|
+
return structuredResult;
|
|
52
61
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
136
|
+
const total = structuredResults.length;
|
|
137
|
+
return {
|
|
138
|
+
resourceLinks,
|
|
139
|
+
structuredResults,
|
|
108
140
|
summary: {
|
|
109
|
-
total
|
|
141
|
+
total,
|
|
110
142
|
succeeded,
|
|
111
|
-
failed,
|
|
143
|
+
failed: total - succeeded,
|
|
112
144
|
},
|
|
145
|
+
text: textSections.join('\n\n'),
|
|
113
146
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
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:
|
|
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:
|
|
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(
|
|
181
|
+
progress.complete(`${READ_MANY_TOOL_LABEL}: ${context} • ${suffix}`, finalCurrent);
|
|
158
182
|
return result;
|
|
159
183
|
}
|
|
160
184
|
catch (error) {
|
|
161
|
-
progress.fail(
|
|
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,
|
|
196
|
+
if (registerToolTaskIfAvailable(server, READ_MANY_TOOL_NAME, READ_MANY_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
173
197
|
return;
|
|
174
|
-
server.registerTool(
|
|
198
|
+
server.registerTool(READ_MANY_TOOL_NAME, withDefaultIcons({ ...READ_MANY_TOOL }, options.iconInfo), validatedHandler);
|
|
175
199
|
}
|
package/dist/tools/read.js
CHANGED
|
@@ -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: '
|
|
21
|
+
taskSupport: 'forbidden',
|
|
22
22
|
};
|
|
23
|
-
|
|
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 (
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
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 (${
|
|
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:
|
|
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:
|
|
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:
|
|
103
|
-
|
|
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,
|
|
172
|
+
if (registerToolTaskIfAvailable(server, READ_TOOL_NAME, READ_FILE_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
145
173
|
return;
|
|
146
|
-
server.registerTool(
|
|
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
|
-
|
|
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<
|
|
11
|
+
}) => void): Promise<ToolResponse<SearchAndReplaceOutput>>;
|
|
10
12
|
export declare function registerSearchAndReplaceTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
13
|
+
export {};
|