@j0hanz/filesystem-mcp 2.6.3 → 2.7.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 (148) hide show
  1. package/README.md +28 -19
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +5 -2
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +22 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/concurrency.d.ts +0 -17
  9. package/dist/core/concurrency.d.ts.map +1 -1
  10. package/dist/core/concurrency.js +53 -47
  11. package/dist/core/concurrency.js.map +1 -1
  12. package/dist/core/config.d.ts +7 -0
  13. package/dist/core/config.d.ts.map +1 -1
  14. package/dist/core/config.js +7 -0
  15. package/dist/core/config.js.map +1 -1
  16. package/dist/core/diff.d.ts +0 -4
  17. package/dist/core/diff.d.ts.map +1 -1
  18. package/dist/core/diff.js +1 -15
  19. package/dist/core/diff.js.map +1 -1
  20. package/dist/core/file-uri.d.ts +9 -9
  21. package/dist/core/file-uri.d.ts.map +1 -1
  22. package/dist/core/file-uri.js +16 -11
  23. package/dist/core/file-uri.js.map +1 -1
  24. package/dist/core/fs.d.ts +1 -1
  25. package/dist/core/fs.d.ts.map +1 -1
  26. package/dist/core/fs.js +65 -56
  27. package/dist/core/fs.js.map +1 -1
  28. package/dist/core/glob.d.ts +6 -0
  29. package/dist/core/glob.d.ts.map +1 -1
  30. package/dist/core/glob.js +80 -13
  31. package/dist/core/glob.js.map +1 -1
  32. package/dist/core/input-required.d.ts +30 -13
  33. package/dist/core/input-required.d.ts.map +1 -1
  34. package/dist/core/input-required.js +35 -10
  35. package/dist/core/input-required.js.map +1 -1
  36. package/dist/core/mime.d.ts +2 -4
  37. package/dist/core/mime.d.ts.map +1 -1
  38. package/dist/core/mime.js +3 -8
  39. package/dist/core/mime.js.map +1 -1
  40. package/dist/core/path-completer.d.ts.map +1 -1
  41. package/dist/core/path-completer.js +7 -4
  42. package/dist/core/path-completer.js.map +1 -1
  43. package/dist/core/path-discovery.d.ts.map +1 -1
  44. package/dist/core/path-discovery.js +2 -2
  45. package/dist/core/path-discovery.js.map +1 -1
  46. package/dist/core/path-utils.d.ts +1 -3
  47. package/dist/core/path-utils.d.ts.map +1 -1
  48. package/dist/core/path-utils.js +0 -3
  49. package/dist/core/path-utils.js.map +1 -1
  50. package/dist/core/path.d.ts +7 -0
  51. package/dist/core/path.d.ts.map +1 -1
  52. package/dist/core/path.js +69 -26
  53. package/dist/core/path.js.map +1 -1
  54. package/dist/core/read.d.ts +1 -0
  55. package/dist/core/read.d.ts.map +1 -1
  56. package/dist/core/read.js +4 -0
  57. package/dist/core/read.js.map +1 -1
  58. package/dist/core/schema.d.ts +0 -80
  59. package/dist/core/schema.d.ts.map +1 -1
  60. package/dist/core/schema.js +0 -89
  61. package/dist/core/schema.js.map +1 -1
  62. package/dist/core/search.d.ts +8 -1
  63. package/dist/core/search.d.ts.map +1 -1
  64. package/dist/core/search.js +191 -101
  65. package/dist/core/search.js.map +1 -1
  66. package/dist/core/store.d.ts +7 -11
  67. package/dist/core/store.d.ts.map +1 -1
  68. package/dist/core/store.js +19 -21
  69. package/dist/core/store.js.map +1 -1
  70. package/dist/core/util.d.ts +17 -0
  71. package/dist/core/util.d.ts.map +1 -1
  72. package/dist/core/util.js +19 -0
  73. package/dist/core/util.js.map +1 -1
  74. package/dist/instructions.js +1 -1
  75. package/dist/instructions.js.map +1 -1
  76. package/dist/resources.d.ts.map +1 -1
  77. package/dist/resources.js +5 -2
  78. package/dist/resources.js.map +1 -1
  79. package/dist/server.d.ts.map +1 -1
  80. package/dist/server.js +8 -1
  81. package/dist/server.js.map +1 -1
  82. package/dist/tools/batch.d.ts +3 -13
  83. package/dist/tools/batch.d.ts.map +1 -1
  84. package/dist/tools/batch.js +11 -27
  85. package/dist/tools/batch.js.map +1 -1
  86. package/dist/tools/create.d.ts.map +1 -1
  87. package/dist/tools/create.js +25 -46
  88. package/dist/tools/create.js.map +1 -1
  89. package/dist/tools/define.d.ts +20 -23
  90. package/dist/tools/define.d.ts.map +1 -1
  91. package/dist/tools/define.js +11 -27
  92. package/dist/tools/define.js.map +1 -1
  93. package/dist/tools/delete.d.ts.map +1 -1
  94. package/dist/tools/delete.js +10 -37
  95. package/dist/tools/delete.js.map +1 -1
  96. package/dist/tools/diff.d.ts.map +1 -1
  97. package/dist/tools/diff.js +2 -13
  98. package/dist/tools/diff.js.map +1 -1
  99. package/dist/tools/edit.d.ts.map +1 -1
  100. package/dist/tools/edit.js +20 -53
  101. package/dist/tools/edit.js.map +1 -1
  102. package/dist/tools/find-files.d.ts.map +1 -1
  103. package/dist/tools/find-files.js +3 -28
  104. package/dist/tools/find-files.js.map +1 -1
  105. package/dist/tools/index.js +1 -1
  106. package/dist/tools/index.js.map +1 -1
  107. package/dist/tools/list-roots.d.ts.map +1 -1
  108. package/dist/tools/list-roots.js +1 -12
  109. package/dist/tools/list-roots.js.map +1 -1
  110. package/dist/tools/list.d.ts.map +1 -1
  111. package/dist/tools/list.js +19 -53
  112. package/dist/tools/list.js.map +1 -1
  113. package/dist/tools/move.d.ts.map +1 -1
  114. package/dist/tools/move.js +54 -37
  115. package/dist/tools/move.js.map +1 -1
  116. package/dist/tools/patch.d.ts.map +1 -1
  117. package/dist/tools/patch.js +4 -24
  118. package/dist/tools/patch.js.map +1 -1
  119. package/dist/tools/progress.d.ts +0 -2
  120. package/dist/tools/progress.d.ts.map +1 -1
  121. package/dist/tools/progress.js +20 -12
  122. package/dist/tools/progress.js.map +1 -1
  123. package/dist/tools/read.d.ts.map +1 -1
  124. package/dist/tools/read.js +29 -116
  125. package/dist/tools/read.js.map +1 -1
  126. package/dist/tools/replace-text.d.ts.map +1 -1
  127. package/dist/tools/replace-text.js +20 -43
  128. package/dist/tools/replace-text.js.map +1 -1
  129. package/dist/tools/search-text.d.ts.map +1 -1
  130. package/dist/tools/search-text.js +24 -78
  131. package/dist/tools/search-text.js.map +1 -1
  132. package/dist/tools/stat.d.ts.map +1 -1
  133. package/dist/tools/stat.js +5 -19
  134. package/dist/tools/stat.js.map +1 -1
  135. package/dist/transport/http-policy.d.ts.map +1 -1
  136. package/dist/transport/http-policy.js +1 -0
  137. package/dist/transport/http-policy.js.map +1 -1
  138. package/dist/transport/http.d.ts.map +1 -1
  139. package/dist/transport/http.js +16 -27
  140. package/dist/transport/http.js.map +1 -1
  141. package/dist/transport/shared.d.ts +3 -1
  142. package/dist/transport/shared.d.ts.map +1 -1
  143. package/dist/transport/shared.js +14 -1
  144. package/dist/transport/shared.js.map +1 -1
  145. package/dist/transport/stdio.d.ts.map +1 -1
  146. package/dist/transport/stdio.js +18 -3
  147. package/dist/transport/stdio.js.map +1 -1
  148. package/package.json +3 -3
@@ -2,11 +2,11 @@ import { createHash } from 'node:crypto';
2
2
  import { basename } from 'node:path';
3
3
  import * as z from 'zod/v4';
4
4
  import { processInParallel } from "../core/concurrency.js";
5
- import { ErrorCode } from "../core/errors.js";
5
+ import { ErrorCode, FsError } from "../core/errors.js";
6
6
  import { buildFileResourceLinkFor, buildFileResourceUri } from "../core/file-uri.js";
7
7
  import { detectMimeFromContent, detectMimeType } from "../core/mime.js";
8
8
  import { readFileWithStats } from "../core/read.js";
9
- import { ContinuationSchema, defaultFalseBoolean, FileKind, NonNegInt, OperationSummarySchema, perPathEnvelope, PositiveInt, Sha256Hex, singleOrBatchAccessPaths, singleOrBatchPathsInput, validateReadRange, } from "../core/schema.js";
9
+ import { defaultFalseBoolean, singleOrBatchAccessPaths, singleOrBatchPathsInput, validateReadRange, } from "../core/schema.js";
10
10
  import { DEFAULT_CONTINUATION_CHUNK_SIZE, DEFAULT_SEARCH_TIMEOUT_MS, getMaxTextFileSize, PARALLEL_CONCURRENCY, READ_MANY_MAX_TOTAL_BYTES, } from "../core/util.js";
11
11
  import { isTotalFailure, runOverPaths } from "./batch.js";
12
12
  import { defineTool } from "./define.js";
@@ -50,35 +50,6 @@ const ReadFileInputSchema = singleOrBatchPathsInput({
50
50
  oneOf: [{ required: ['path'] }, { required: ['paths'] }],
51
51
  dependentRequired: { endLine: ['startLine'] },
52
52
  });
53
- // File bytes are NOT here: text rides the text content block, image/audio ride
54
- // a media block. This schema is the metadata a caller cannot recover from
55
- // those blocks. `readOnePath` carries the bytes internally via
56
- // `PerPathReadValue` and `run` drops them before the structured half goes out.
57
- const ReadPerPathValueSchema = z.strictObject({
58
- mimeType: z.string().optional().describe('Detected MIME type (e.g. text/typescript)'),
59
- kind: FileKind.optional().describe('Broad file kind: text, binary, image, audio, or pdf'),
60
- resourceUri: z
61
- .string()
62
- .optional()
63
- .describe('Resource URI for externalized content (present when file is stored in resource store)'),
64
- continuation: ContinuationSchema.optional().describe('Next-read arguments; present when content was truncated due to size limits'),
65
- totalLines: NonNegInt.optional().describe('Total line count in the full file'),
66
- linesRead: NonNegInt.optional().describe('Number of lines returned in this response'),
67
- hasMoreLines: z
68
- .boolean()
69
- .optional()
70
- .describe('True when additional lines remain beyond what was returned'),
71
- head: PositiveInt.optional().describe('Head lines requested'),
72
- tail: PositiveInt.optional().describe('Tail lines requested'),
73
- startLine: PositiveInt.optional().describe('Start line'),
74
- endLine: PositiveInt.optional().describe('End line'),
75
- contentHash: Sha256Hex.optional().describe('SHA-256 hex digest of the returned content (present when includeHash=true)'),
76
- });
77
- const ReadPerPathSchema = perPathEnvelope(ReadPerPathValueSchema, 'Read result; present on success');
78
- const ReadFileOutputSchema = z.strictObject({
79
- results: z.array(ReadPerPathSchema).describe('Per-path results ordered to match the input paths'),
80
- summary: OperationSummarySchema,
81
- });
82
53
  const READ_TOOL_LABEL = 'Read';
83
54
  function buildReadSpec(args, signal) {
84
55
  const signalPart = signal ? { signal } : {};
@@ -134,54 +105,32 @@ async function collectFileBudget(filePaths, maxTotalSize, maxSize, ctx) {
134
105
  };
135
106
  }
136
107
  catch (err) {
137
- ctx.log?.('debug', `collectFileBudget: stat failed for "${path}": ${String(err)}`, 'read');
108
+ ctx.log('debug', `collectFileBudget: stat failed for "${path}": ${String(err)}`, 'read');
138
109
  return undefined;
139
110
  }
140
111
  }, PARALLEL_CONCURRENCY, ctx.signal);
141
112
  const byIndex = new Map();
142
- const known = new Map();
143
113
  for (const { value: item } of results) {
144
- if (!item)
145
- continue;
146
- byIndex.set(item.index, item.size);
147
- // Keyed by the ORIGINAL requested path string (not validPath) — that's
148
- // what readOnePath is called with downstream, and what filePaths[i]
149
- // holds.
150
- const requestedPath = filePaths[item.index];
151
- if (requestedPath !== undefined) {
152
- known.set(requestedPath, { validPath: item.validPath, stats: item.stats });
153
- }
114
+ if (item)
115
+ byIndex.set(item.index, item);
154
116
  }
155
117
  let total = 0;
156
- const skippedResults = new Map();
157
- const survivors = [];
158
- for (let i = 0; i < filePaths.length; i += 1) {
159
- const path = filePaths[i];
160
- if (path === undefined)
161
- continue;
162
- const size = byIndex.get(i);
163
- // A failed stat falls through to survivors so its read surfaces the real
164
- // error, not a misleading TOO_LARGE.
165
- if (size === undefined) {
166
- survivors.push(path);
167
- continue;
168
- }
118
+ return filePaths.map((path, i) => {
119
+ const info = byIndex.get(i);
120
+ // A failed stat is neither known nor skipped, so its read surfaces the
121
+ // real error, not a misleading TOO_LARGE.
122
+ if (info === undefined)
123
+ return { path };
169
124
  // Skip this file only; a later, smaller file that still fits is read.
170
- if (total + size > maxTotalSize) {
171
- skippedResults.set(i, {
125
+ if (total + info.size > maxTotalSize) {
126
+ return {
172
127
  path,
173
- error: {
174
- code: ErrorCode.TOO_LARGE,
175
- message: `Skipped: this file alone would push the batch past maxTotalSize (${String(maxTotalSize)} bytes). Read it on its own with path instead of paths.`,
176
- path,
177
- },
178
- });
179
- continue;
128
+ skip: new FsError(ErrorCode.TOO_LARGE, `Skipped: this file alone would push the batch past maxTotalSize (${String(maxTotalSize)} bytes). Read it on its own with path instead of paths.`, path),
129
+ };
180
130
  }
181
- total += size;
182
- survivors.push(path);
183
- }
184
- return { skippedResults, survivors, known };
131
+ total += info.size;
132
+ return { path, known: { validPath: info.validPath, stats: info.stats } };
133
+ });
185
134
  }
186
135
  function buildPerPathReadValue(result, options) {
187
136
  const mimeInfo = detectMimeFromContent(result.path, result.content);
@@ -191,7 +140,6 @@ function buildPerPathReadValue(result, options) {
191
140
  const contentHash = options.includeHash
192
141
  ? createHash('sha256').update(result.content, 'utf-8').digest('hex')
193
142
  : undefined;
194
- const resourceUri = options.hasResourceStore ? buildFileResourceUri(result.path) : undefined;
195
143
  return {
196
144
  content: result.content,
197
145
  mimeType: mimeInfo.mimeType,
@@ -205,7 +153,8 @@ function buildPerPathReadValue(result, options) {
205
153
  ...(result.startLine !== undefined ? { startLine: result.startLine } : {}),
206
154
  ...(result.endLine !== undefined ? { endLine: result.endLine } : {}),
207
155
  ...(contentHash !== undefined ? { contentHash } : {}),
208
- ...(resourceUri !== undefined ? { resourceUri } : {}),
156
+ resourceUri: buildFileResourceUri(result.path),
157
+ modified: result.modified,
209
158
  };
210
159
  }
211
160
  /**
@@ -260,7 +209,6 @@ async function readOnePath(filePath, args, ctx, known) {
260
209
  : await ctx.fs.readFile(filePath, spec);
261
210
  return buildPerPathReadValue(result, {
262
211
  includeHash: args.includeHash,
263
- hasResourceStore: ctx.resourceStore !== undefined,
264
212
  // The continuation is args the caller pastes back, so it echoes the path
265
213
  // they wrote. `result.path` is the resolved absolute one — on Windows that
266
214
  // is a backslash-doubling JSON string that also spells out the server root.
@@ -273,11 +221,7 @@ export const READ = defineTool({
273
221
  description: 'Read text files, like cat: whole, or one slice via head, tail, or startLine/endLine. ' +
274
222
  'Images and audio return as media; other binary files are rejected.',
275
223
  input: ReadFileInputSchema,
276
- output: ReadFileOutputSchema,
277
- annotations: {
278
- readOnlyHint: true,
279
- openWorldHint: false,
280
- },
224
+ readOnlyHint: true,
281
225
  timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,
282
226
  defaultErrorCode: ErrorCode.NOT_FILE,
283
227
  progress: (args) => {
@@ -301,45 +245,14 @@ export const READ = defineTool({
301
245
  },
302
246
  accessPaths: singleOrBatchAccessPaths,
303
247
  run: async (args, ctx) => {
304
- let pathList;
305
- let skippedResults = new Map();
306
- let survivors;
307
- let known = new Map();
308
- if (args.paths !== undefined) {
309
- pathList = args.paths;
310
- const budget = await collectFileBudget(pathList, READ_MANY_MAX_TOTAL_BYTES, getMaxTextFileSize(), ctx);
311
- known = budget.known;
312
- skippedResults = budget.skippedResults;
313
- survivors = budget.survivors;
314
- }
315
- else {
316
- pathList = [args.path ?? ''];
317
- survivors = [...pathList];
318
- }
319
- const batchInput = { paths: survivors };
320
- // Every path can be budget-skipped (a single file over maxTotalSize does
321
- // it), and runOverPaths rejects an empty list. The per-path TOO_LARGE
322
- // results are already built — return those rather than failing the call.
323
- const batch = survivors.length === 0
324
- ? { results: [] }
325
- : await runOverPaths(batchInput, ctx, ErrorCode.NOT_FILE, ({ path }) => readOnePath(path, args, ctx, known.get(path)));
326
- const resultMap = new Map(batch.results.map((r) => [r.path, r]));
327
- const ordered = pathList.map((path, idx) => {
328
- const skipped = skippedResults.get(idx);
329
- if (skipped)
330
- return skipped;
331
- const result = resultMap.get(path);
332
- return (result ?? {
333
- path,
334
- error: { code: ErrorCode.UNKNOWN, message: 'Unknown read failure' },
335
- });
248
+ const items = args.paths !== undefined
249
+ ? await collectFileBudget(args.paths, READ_MANY_MAX_TOTAL_BYTES, getMaxTextFileSize(), ctx)
250
+ : [{ path: args.path ?? '' }];
251
+ const { results: ordered, summary } = await runOverPaths(items, ctx, ErrorCode.NOT_FILE, ({ path, known, skip }) => {
252
+ if (skip)
253
+ throw skip;
254
+ return readOnePath(path, args, ctx, known);
336
255
  });
337
- const failed = ordered.filter((r) => 'error' in r).length;
338
- const summary = {
339
- total: ordered.length,
340
- succeeded: ordered.length - failed,
341
- failed,
342
- };
343
256
  const resources = [];
344
257
  for (const result of ordered) {
345
258
  if ('error' in result)
@@ -359,7 +272,7 @@ export const READ = defineTool({
359
272
  // the *validated* one. Rebuilding from the request would emit a second URI
360
273
  // for the same file whenever the two differ in case — subscribe by one and
361
274
  // unsubscribe by the other and the watcher never goes away.
362
- resources.push(buildFileResourceLinkFor(v.resourceUri, basename(result.path), v.mimeType ?? 'application/octet-stream', Buffer.byteLength(v.content, 'utf8')));
275
+ resources.push(buildFileResourceLinkFor(v.resourceUri, basename(result.path), v.mimeType ?? 'application/octet-stream', Buffer.byteLength(v.content, 'utf8'), v.modified));
363
276
  }
364
277
  const [firstOrdered] = ordered;
365
278
  const body = (v) => {
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { createHash } from 'node:crypto';\nimport type { Stats } from 'node:fs';\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport { ErrorCode } from '../core/errors.ts';\nimport { buildFileResourceLinkFor, buildFileResourceUri } from '../core/file-uri.ts';\nimport { detectMimeFromContent, detectMimeType } from '../core/mime.ts';\nimport type { ReadFileResult, ReadSpec } from '../core/read.ts';\nimport { readFileWithStats } from '../core/read.ts';\nimport {\n ContinuationSchema,\n defaultFalseBoolean,\n FileKind,\n NonNegInt,\n OperationSummarySchema,\n perPathEnvelope,\n PositiveInt,\n Sha256Hex,\n singleOrBatchAccessPaths,\n singleOrBatchPathsInput,\n validateReadRange,\n} from '../core/schema.ts';\nimport {\n DEFAULT_CONTINUATION_CHUNK_SIZE,\n DEFAULT_SEARCH_TIMEOUT_MS,\n getMaxTextFileSize,\n PARALLEL_CONCURRENCY,\n READ_MANY_MAX_TOTAL_BYTES,\n} from '../core/util.ts';\nimport type { PerPathResult } from './batch.ts';\nimport { isTotalFailure, runOverPaths } from './batch.ts';\nimport type { ToolCtx } from './define.ts';\nimport { defineTool } from './define.ts';\n\nconst rangeField = (description: string) =>\n z\n .int32()\n .min(1, { message: 'Min: 1' })\n .max(100000, { message: 'Max: 100,000' })\n .optional()\n .describe(description);\n\nconst readRangeFields = {\n head: rangeField('Return first N lines'),\n tail: rangeField('Return last N lines'),\n startLine: rangeField('Start line (1-indexed)'),\n endLine: rangeField('End line (1-indexed)'),\n};\n\nconst ReadFileInputSchema = singleOrBatchPathsInput({\n includeHash: defaultFalseBoolean('Include SHA-256 hash of the returned content in the response'),\n ...readRangeFields,\n})\n .superRefine((value, ctx) => {\n validateReadRange(\n {\n head: value.head,\n tail: value.tail,\n startLine: value.startLine,\n endLine: value.endLine,\n },\n ctx,\n );\n })\n // Mirror `validateReadRange` on the wire. Without this the four line params\n // published as four independent optional integers, so `{ path, head, tail }`\n // passed the advertised schema and was rejected only at call time.\n //\n // `oneOf` is restated because `.meta()` replaces the entry\n // `singleOrBatchPathsInput` set rather than merging into it — dropping it here\n // would silently un-publish the path/paths rule.\n .meta({\n // The line-param exclusivity that used to sit here as a five-branch\n // `not/anyOf` is one sentence in the description instead: validateReadRange\n // rejects every conflicting combination by name at runtime, so the wire copy\n // was paying for a second statement of a rule the model reads better in\n // prose.\n oneOf: [{ required: ['path'] }, { required: ['paths'] }],\n dependentRequired: { endLine: ['startLine'] },\n });\n\n// File bytes are NOT here: text rides the text content block, image/audio ride\n// a media block. This schema is the metadata a caller cannot recover from\n// those blocks. `readOnePath` carries the bytes internally via\n// `PerPathReadValue` and `run` drops them before the structured half goes out.\nconst ReadPerPathValueSchema = z.strictObject({\n mimeType: z.string().optional().describe('Detected MIME type (e.g. text/typescript)'),\n kind: FileKind.optional().describe('Broad file kind: text, binary, image, audio, or pdf'),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'Resource URI for externalized content (present when file is stored in resource store)',\n ),\n continuation: ContinuationSchema.optional().describe(\n 'Next-read arguments; present when content was truncated due to size limits',\n ),\n totalLines: NonNegInt.optional().describe('Total line count in the full file'),\n linesRead: NonNegInt.optional().describe('Number of lines returned in this response'),\n hasMoreLines: z\n .boolean()\n .optional()\n .describe('True when additional lines remain beyond what was returned'),\n head: PositiveInt.optional().describe('Head lines requested'),\n tail: PositiveInt.optional().describe('Tail lines requested'),\n startLine: PositiveInt.optional().describe('Start line'),\n endLine: PositiveInt.optional().describe('End line'),\n contentHash: Sha256Hex.optional().describe(\n 'SHA-256 hex digest of the returned content (present when includeHash=true)',\n ),\n});\n\nconst ReadPerPathSchema = perPathEnvelope(\n ReadPerPathValueSchema,\n 'Read result; present on success',\n);\n\nconst ReadFileOutputSchema = z.strictObject({\n results: z.array(ReadPerPathSchema).describe('Per-path results ordered to match the input paths'),\n summary: OperationSummarySchema,\n});\n\ntype ReadFileInput = z.infer<typeof ReadFileInputSchema>;\n\nconst READ_TOOL_LABEL = 'Read';\n\nfunction buildReadSpec(args: ReadFileInput, signal?: AbortSignal): ReadSpec {\n const signalPart = signal ? { signal } : {};\n const { head, tail, startLine, endLine } = args;\n\n if (head !== undefined) return { kind: 'head', lines: head, ...signalPart };\n if (tail !== undefined) return { kind: 'tail', lines: tail, ...signalPart };\n if (startLine !== undefined || endLine !== undefined) {\n return {\n kind: 'range',\n start: startLine ?? 1,\n ...(endLine !== undefined ? { end: endLine } : {}),\n ...signalPart,\n };\n }\n return { kind: 'full', ...signalPart };\n}\n\n// No `totalLines`: only `readFull` counts them and a full read never leaves\n// more behind, so a continuation cannot know the total without a second pass.\nfunction buildReadContinuation(\n result: ReadFileResult,\n requestedPath: string,\n): z.infer<typeof ContinuationSchema> | undefined {\n if (!result.hasMoreLines) return undefined;\n const linesRead = result.linesRead ?? 0;\n const nextStart = (result.startLine ?? 1) + linesRead;\n let chunkSize: number;\n if (result.head !== undefined) {\n chunkSize = result.head;\n } else if (result.startLine !== undefined && result.endLine !== undefined) {\n chunkSize = result.endLine - result.startLine + 1;\n } else {\n chunkSize = DEFAULT_CONTINUATION_CHUNK_SIZE;\n }\n const nextEnd = nextStart + chunkSize - 1;\n return {\n tool: 'read',\n args: { path: requestedPath, startLine: nextStart, endLine: nextEnd },\n hint: `More lines remain from ${String(nextStart)}. Read next chunk with these args.`,\n };\n}\n\n/**\n * The published metadata plus the bytes this call read. `content` and\n * `mediaData` exist only between `readOnePath` and the content blocks `run`\n * builds from them — they are stripped before the structured half is returned,\n * which is why they are not in `ReadPerPathValueSchema`.\n */\ntype PerPathReadValue = z.infer<typeof ReadPerPathValueSchema> & {\n content?: string;\n mediaData?: string;\n};\n\ninterface BatchFileInfo {\n index: number;\n size: number;\n validPath: string;\n stats: Stats;\n}\n\nasync function collectFileBudget(\n filePaths: readonly string[],\n maxTotalSize: number,\n maxSize: number,\n ctx: Pick<ToolCtx, 'fs' | 'signal' | 'log'>,\n): Promise<{\n skippedResults: Map<number, PerPathResult<PerPathReadValue>>;\n survivors: string[];\n known: Map<string, { validPath: string; stats: Stats }>;\n}> {\n const indexed = filePaths.map((path, index) => ({ path, index }));\n const { results } = await processInParallel(\n indexed,\n async ({ path, index }): Promise<BatchFileInfo | undefined> => {\n try {\n const out = await ctx.fs.stat(path);\n return {\n index,\n size: Math.min(out.stats.size, maxSize),\n validPath: out.validPath,\n stats: out.stats,\n };\n } catch (err: unknown) {\n ctx.log?.('debug', `collectFileBudget: stat failed for \"${path}\": ${String(err)}`, 'read');\n return undefined;\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n const byIndex = new Map<number, number>();\n const known = new Map<string, { validPath: string; stats: Stats }>();\n for (const { value: item } of results) {\n if (!item) continue;\n byIndex.set(item.index, item.size);\n // Keyed by the ORIGINAL requested path string (not validPath) — that's\n // what readOnePath is called with downstream, and what filePaths[i]\n // holds.\n const requestedPath = filePaths[item.index];\n if (requestedPath !== undefined) {\n known.set(requestedPath, { validPath: item.validPath, stats: item.stats });\n }\n }\n\n let total = 0;\n const skippedResults = new Map<number, PerPathResult<PerPathReadValue>>();\n const survivors: string[] = [];\n for (let i = 0; i < filePaths.length; i += 1) {\n const path = filePaths[i];\n if (path === undefined) continue;\n const size = byIndex.get(i);\n // A failed stat falls through to survivors so its read surfaces the real\n // error, not a misleading TOO_LARGE.\n if (size === undefined) {\n survivors.push(path);\n continue;\n }\n // Skip this file only; a later, smaller file that still fits is read.\n if (total + size > maxTotalSize) {\n skippedResults.set(i, {\n path,\n error: {\n code: ErrorCode.TOO_LARGE,\n message: `Skipped: this file alone would push the batch past maxTotalSize (${String(maxTotalSize)} bytes). Read it on its own with path instead of paths.`,\n path,\n },\n });\n continue;\n }\n total += size;\n survivors.push(path);\n }\n\n return { skippedResults, survivors, known };\n}\n\nfunction buildPerPathReadValue(\n result: ReadFileResult,\n options: { includeHash?: boolean; hasResourceStore?: boolean; requestedPath: string },\n): PerPathReadValue {\n const mimeInfo = detectMimeFromContent(result.path, result.content);\n const continuation =\n result.hasMoreLines && result.readMode !== 'tail'\n ? buildReadContinuation(result, options.requestedPath)\n : undefined;\n const contentHash = options.includeHash\n ? createHash('sha256').update(result.content, 'utf-8').digest('hex')\n : undefined;\n const resourceUri = options.hasResourceStore ? buildFileResourceUri(result.path) : undefined;\n\n return {\n content: result.content,\n mimeType: mimeInfo.mimeType,\n kind: mimeInfo.kind,\n ...(continuation ? { continuation } : {}),\n ...(result.totalLines !== undefined ? { totalLines: result.totalLines } : {}),\n ...(result.linesRead !== undefined ? { linesRead: result.linesRead } : {}),\n ...(result.hasMoreLines ? { hasMoreLines: true } : {}),\n ...(result.head !== undefined ? { head: result.head } : {}),\n ...(result.tail !== undefined ? { tail: result.tail } : {}),\n ...(result.startLine !== undefined ? { startLine: result.startLine } : {}),\n ...(result.endLine !== undefined ? { endLine: result.endLine } : {}),\n ...(contentHash !== undefined ? { contentHash } : {}),\n ...(resourceUri !== undefined ? { resourceUri } : {}),\n };\n}\n\n/**\n * The two facts the model cannot act on without: that lines were dropped, and\n * the exact args to get the next chunk. Both used to live only in the structured\n * half, which no longer reaches the model. Each line is `//` prefixed and set\n * off by a blank line so it cannot be read as file bytes — do not drop either\n * guard.\n */\nfunction readTrailer(v: PerPathReadValue): string[] {\n const lines: string[] = [];\n if (v.continuation) {\n lines.push(\n `// truncated: ${v.continuation.hint} Continue: read ${JSON.stringify(v.continuation.args)}`,\n );\n } else if (v.hasMoreLines && v.linesRead !== undefined) {\n // A tail read gets no continuation — there are no args that read *backwards*\n // — so say what was shown rather than leaving the truncation silent.\n lines.push(`// truncated: showing last ${String(v.linesRead)} lines`);\n }\n if (v.contentHash !== undefined) lines.push(`// sha256: ${v.contentHash}`);\n return lines;\n}\n\nasync function readOnePath(\n filePath: string,\n args: ReadFileInput,\n ctx: ToolCtx,\n known?: { validPath: string; stats: Stats },\n): Promise<PerPathReadValue> {\n // Image/audio full read: return a media content block instead of throwing\n // INVALID_INPUT (\"Binary file detected.\"). Line-range reads are\n // text-oriented and stay rejected. `readRaw` enforces the same size cap as\n // the text path (getMaxTextFileSize), so a too-large image surfaces TOO_LARGE\n // rather than blowing memory. svg carries kind:'image' but is XML text —\n // readRaw.isBinary is false for it, so it falls through to the text path.\n const isRangeRead =\n args.head !== undefined ||\n args.tail !== undefined ||\n args.startLine !== undefined ||\n args.endLine !== undefined;\n if (!isRangeRead) {\n const mime = detectMimeType(filePath);\n if (mime.kind === 'image' || mime.kind === 'audio') {\n const raw = await ctx.fs.readRaw(filePath, { signal: ctx.signal });\n if (raw.isBinary) {\n return {\n content: `[binary ${mime.kind} content: ${String(raw.content.length)} bytes; returned as a ${mime.kind} content block]`,\n mimeType: raw.mimeType,\n kind: mime.kind,\n mediaData: raw.content.toString('base64'),\n };\n }\n }\n }\n\n const spec = buildReadSpec(args, ctx.signal);\n const result = known\n ? await readFileWithStats(filePath, known.validPath, known.stats, spec)\n : await ctx.fs.readFile(filePath, spec);\n\n return buildPerPathReadValue(result, {\n includeHash: args.includeHash,\n hasResourceStore: ctx.resourceStore !== undefined,\n // The continuation is args the caller pastes back, so it echoes the path\n // they wrote. `result.path` is the resolved absolute one — on Windows that\n // is a backslash-doubling JSON string that also spells out the server root.\n requestedPath: filePath,\n });\n}\n\nexport const READ = defineTool({\n name: 'read',\n title: 'Read File',\n description:\n 'Read text files, like cat: whole, or one slice via head, tail, or startLine/endLine. ' +\n 'Images and audio return as media; other binary files are rejected.',\n input: ReadFileInputSchema,\n output: ReadFileOutputSchema,\n annotations: {\n readOnlyHint: true,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.NOT_FILE,\n progress: (args) => {\n const isBatch = args.paths !== undefined;\n const name = isBatch ? `${String(args.paths?.length ?? 0)} files` : basename(args.path ?? '');\n if (isBatch) {\n return { label: READ_TOOL_LABEL, subject: name };\n }\n let scope: string | undefined;\n if (args.startLine !== undefined) {\n const end = args.endLine ?? '...';\n scope = `${args.startLine}-${String(end)}`;\n } else if (args.head !== undefined) {\n scope = `head ${args.head}`;\n } else if (args.tail !== undefined) {\n scope = `tail ${args.tail}`;\n }\n return { label: READ_TOOL_LABEL, subject: name, ...(scope ? { scope } : {}) };\n },\n accessPaths: singleOrBatchAccessPaths,\n run: async (args, ctx) => {\n let pathList: string[];\n let skippedResults = new Map<number, PerPathResult<PerPathReadValue>>();\n let survivors: string[];\n let known = new Map<string, { validPath: string; stats: Stats }>();\n\n if (args.paths !== undefined) {\n pathList = args.paths;\n const budget = await collectFileBudget(\n pathList,\n READ_MANY_MAX_TOTAL_BYTES,\n getMaxTextFileSize(),\n ctx,\n );\n known = budget.known;\n skippedResults = budget.skippedResults;\n survivors = budget.survivors;\n } else {\n pathList = [args.path ?? ''];\n survivors = [...pathList];\n }\n\n const batchInput = { paths: survivors };\n\n // Every path can be budget-skipped (a single file over maxTotalSize does\n // it), and runOverPaths rejects an empty list. The per-path TOO_LARGE\n // results are already built — return those rather than failing the call.\n const batch =\n survivors.length === 0\n ? { results: [] as PerPathResult<PerPathReadValue>[] }\n : await runOverPaths<undefined, PerPathReadValue>(\n batchInput,\n ctx,\n ErrorCode.NOT_FILE,\n ({ path }) => readOnePath(path, args, ctx, known.get(path)),\n );\n\n const resultMap = new Map(batch.results.map((r) => [r.path, r]));\n const ordered: PerPathResult<PerPathReadValue>[] = pathList.map((path, idx) => {\n const skipped = skippedResults.get(idx);\n if (skipped) return skipped;\n const result = resultMap.get(path);\n return (\n result ?? {\n path,\n error: { code: ErrorCode.UNKNOWN, message: 'Unknown read failure' },\n }\n );\n });\n\n const failed = ordered.filter((r) => 'error' in r).length;\n const summary = {\n total: ordered.length,\n succeeded: ordered.length - failed,\n failed,\n };\n\n const resources: ContentBlock[] = [];\n for (const result of ordered) {\n if ('error' in result) continue;\n const v = result.value;\n // Image/audio full read: emit the media content block alongside the\n // structured value. The base64 lives in v.mediaData (set in readOnePath).\n if (v.mediaData && v.mimeType && (v.kind === 'image' || v.kind === 'audio')) {\n resources.push(\n v.kind === 'image'\n ? { type: 'image', data: v.mediaData, mimeType: v.mimeType }\n : { type: 'audio', data: v.mediaData, mimeType: v.mimeType },\n );\n continue;\n }\n if (!v.resourceUri || !v.content) continue;\n // `result.path` is the path as *requested*; `v.resourceUri` was built from\n // the *validated* one. Rebuilding from the request would emit a second URI\n // for the same file whenever the two differ in case — subscribe by one and\n // unsubscribe by the other and the watcher never goes away.\n resources.push(\n buildFileResourceLinkFor(\n v.resourceUri,\n basename(result.path),\n v.mimeType ?? 'application/octet-stream',\n Buffer.byteLength(v.content, 'utf8'),\n ),\n );\n }\n\n const [firstOrdered] = ordered;\n const body = (v: PerPathReadValue): string => {\n const content = v.content ?? 'read failed';\n const trailer = readTrailer(v);\n if (trailer.length === 0) return content;\n // Exactly one blank line between the bytes and the trailer, whether or not\n // the file ended in a newline.\n return `${content}${content.endsWith('\\n') ? '\\n' : '\\n\\n'}${trailer.join('\\n')}`;\n };\n const text =\n ordered.length === 1 && firstOrdered !== undefined\n ? 'error' in firstOrdered\n ? firstOrdered.error.message\n : body(firstOrdered.value)\n : ordered\n .map((r) => {\n const header = `// ${r.path}`;\n if ('value' in r) return `${header}\\n${body(r.value)}`;\n return `${header}\\n// Error: ${r.error.message}`;\n })\n .join('\\n\\n');\n\n // The bytes ship once. `text` above already carries every file's content\n // verbatim (and image/audio bytes ride the media content block), so\n // repeating them under `results[].value` doubled both the wire payload and\n // the model's token cost for every read. The structured half keeps the\n // metadata a client cannot recover from the text: paths, line counts,\n // hashes, continuations, resourceUri.\n const structuredResults = ordered.map((result) => {\n if ('error' in result) return result;\n const { content: _content, mediaData: _mediaData, ...value } = result.value;\n return { ...result, value };\n });\n\n return {\n structured: { results: structuredResults, summary },\n text,\n isError: isTotalFailure(summary),\n ...(resources.length > 0 ? { resources } : {}),\n };\n },\n});\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { createHash } from 'node:crypto';\nimport type { Stats } from 'node:fs';\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport { ErrorCode, FsError } from '../core/errors.ts';\nimport { buildFileResourceLinkFor, buildFileResourceUri } from '../core/file-uri.ts';\nimport { detectMimeFromContent, detectMimeType, type FileKind } from '../core/mime.ts';\nimport type { ReadFileResult, ReadSpec } from '../core/read.ts';\nimport { readFileWithStats } from '../core/read.ts';\nimport {\n defaultFalseBoolean,\n singleOrBatchAccessPaths,\n singleOrBatchPathsInput,\n validateReadRange,\n} from '../core/schema.ts';\nimport {\n DEFAULT_CONTINUATION_CHUNK_SIZE,\n DEFAULT_SEARCH_TIMEOUT_MS,\n getMaxTextFileSize,\n PARALLEL_CONCURRENCY,\n READ_MANY_MAX_TOTAL_BYTES,\n} from '../core/util.ts';\nimport type { BatchResult } from './batch.ts';\nimport { isTotalFailure, runOverPaths } from './batch.ts';\nimport type { ToolCtx } from './define.ts';\nimport { defineTool } from './define.ts';\n\nconst rangeField = (description: string) =>\n z\n .int32()\n .min(1, { message: 'Min: 1' })\n .max(100000, { message: 'Max: 100,000' })\n .optional()\n .describe(description);\n\nconst readRangeFields = {\n head: rangeField('Return first N lines'),\n tail: rangeField('Return last N lines'),\n startLine: rangeField('Start line (1-indexed)'),\n endLine: rangeField('End line (1-indexed)'),\n};\n\nconst ReadFileInputSchema = singleOrBatchPathsInput({\n includeHash: defaultFalseBoolean('Include SHA-256 hash of the returned content in the response'),\n ...readRangeFields,\n})\n .superRefine((value, ctx) => {\n validateReadRange(\n {\n head: value.head,\n tail: value.tail,\n startLine: value.startLine,\n endLine: value.endLine,\n },\n ctx,\n );\n })\n // Mirror `validateReadRange` on the wire. Without this the four line params\n // published as four independent optional integers, so `{ path, head, tail }`\n // passed the advertised schema and was rejected only at call time.\n //\n // `oneOf` is restated because `.meta()` replaces the entry\n // `singleOrBatchPathsInput` set rather than merging into it — dropping it here\n // would silently un-publish the path/paths rule.\n .meta({\n // The line-param exclusivity that used to sit here as a five-branch\n // `not/anyOf` is one sentence in the description instead: validateReadRange\n // rejects every conflicting combination by name at runtime, so the wire copy\n // was paying for a second statement of a rule the model reads better in\n // prose.\n oneOf: [{ required: ['path'] }, { required: ['paths'] }],\n dependentRequired: { endLine: ['startLine'] },\n });\n\ninterface ReadContinuation {\n tool: 'read';\n args: { path: string; startLine: number; endLine: number };\n hint: string;\n}\n\n// File bytes ride content blocks, never the structured metadata.\ninterface ReadPerPathValue {\n mimeType?: string;\n kind?: FileKind;\n resourceUri?: string;\n /** ISO-8601 mtime at read time; also rides the resource_link. */\n modified?: string;\n continuation?: ReadContinuation;\n totalLines?: number;\n linesRead?: number;\n hasMoreLines?: boolean;\n head?: number;\n tail?: number;\n startLine?: number;\n endLine?: number;\n contentHash?: string;\n}\n\ntype ReadFileInput = z.infer<typeof ReadFileInputSchema>;\n\nconst READ_TOOL_LABEL = 'Read';\n\nfunction buildReadSpec(args: ReadFileInput, signal?: AbortSignal): ReadSpec {\n const signalPart = signal ? { signal } : {};\n const { head, tail, startLine, endLine } = args;\n\n if (head !== undefined) return { kind: 'head', lines: head, ...signalPart };\n if (tail !== undefined) return { kind: 'tail', lines: tail, ...signalPart };\n if (startLine !== undefined || endLine !== undefined) {\n return {\n kind: 'range',\n start: startLine ?? 1,\n ...(endLine !== undefined ? { end: endLine } : {}),\n ...signalPart,\n };\n }\n return { kind: 'full', ...signalPart };\n}\n\n// No `totalLines`: only `readFull` counts them and a full read never leaves\n// more behind, so a continuation cannot know the total without a second pass.\nfunction buildReadContinuation(\n result: ReadFileResult,\n requestedPath: string,\n): ReadContinuation | undefined {\n if (!result.hasMoreLines) return undefined;\n const linesRead = result.linesRead ?? 0;\n const nextStart = (result.startLine ?? 1) + linesRead;\n let chunkSize: number;\n if (result.head !== undefined) {\n chunkSize = result.head;\n } else if (result.startLine !== undefined && result.endLine !== undefined) {\n chunkSize = result.endLine - result.startLine + 1;\n } else {\n chunkSize = DEFAULT_CONTINUATION_CHUNK_SIZE;\n }\n const nextEnd = nextStart + chunkSize - 1;\n return {\n tool: 'read',\n args: { path: requestedPath, startLine: nextStart, endLine: nextEnd },\n hint: `More lines remain from ${String(nextStart)}. Read next chunk with these args.`,\n };\n}\n\n/**\n * The published metadata plus the bytes this call read. `content` and\n * `mediaData` exist only between `readOnePath` and the content blocks `run`\n * builds from them — they are stripped before the structured half is returned,\n * which is why they are not in `ReadPerPathValue`.\n */\ntype PerPathReadValue = ReadPerPathValue & {\n content?: string;\n mediaData?: string;\n};\n\ninterface BatchFileInfo {\n index: number;\n size: number;\n validPath: string;\n stats: Stats;\n}\n\ninterface BudgetItem {\n path: string;\n known?: { validPath: string; stats: Stats };\n skip?: FsError;\n}\n\nasync function collectFileBudget(\n filePaths: readonly string[],\n maxTotalSize: number,\n maxSize: number,\n ctx: Pick<ToolCtx, 'fs' | 'signal' | 'log'>,\n): Promise<BudgetItem[]> {\n const indexed = filePaths.map((path, index) => ({ path, index }));\n const { results } = await processInParallel(\n indexed,\n async ({ path, index }): Promise<BatchFileInfo | undefined> => {\n try {\n const out = await ctx.fs.stat(path);\n return {\n index,\n size: Math.min(out.stats.size, maxSize),\n validPath: out.validPath,\n stats: out.stats,\n };\n } catch (err: unknown) {\n ctx.log('debug', `collectFileBudget: stat failed for \"${path}\": ${String(err)}`, 'read');\n return undefined;\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n const byIndex = new Map<number, BatchFileInfo>();\n for (const { value: item } of results) {\n if (item) byIndex.set(item.index, item);\n }\n\n let total = 0;\n return filePaths.map((path, i): BudgetItem => {\n const info = byIndex.get(i);\n // A failed stat is neither known nor skipped, so its read surfaces the\n // real error, not a misleading TOO_LARGE.\n if (info === undefined) return { path };\n // Skip this file only; a later, smaller file that still fits is read.\n if (total + info.size > maxTotalSize) {\n return {\n path,\n skip: new FsError(\n ErrorCode.TOO_LARGE,\n `Skipped: this file alone would push the batch past maxTotalSize (${String(maxTotalSize)} bytes). Read it on its own with path instead of paths.`,\n path,\n ),\n };\n }\n total += info.size;\n return { path, known: { validPath: info.validPath, stats: info.stats } };\n });\n}\n\nfunction buildPerPathReadValue(\n result: ReadFileResult,\n options: { includeHash?: boolean; requestedPath: string },\n): PerPathReadValue {\n const mimeInfo = detectMimeFromContent(result.path, result.content);\n const continuation =\n result.hasMoreLines && result.readMode !== 'tail'\n ? buildReadContinuation(result, options.requestedPath)\n : undefined;\n const contentHash = options.includeHash\n ? createHash('sha256').update(result.content, 'utf-8').digest('hex')\n : undefined;\n\n return {\n content: result.content,\n mimeType: mimeInfo.mimeType,\n kind: mimeInfo.kind,\n ...(continuation ? { continuation } : {}),\n ...(result.totalLines !== undefined ? { totalLines: result.totalLines } : {}),\n ...(result.linesRead !== undefined ? { linesRead: result.linesRead } : {}),\n ...(result.hasMoreLines ? { hasMoreLines: true } : {}),\n ...(result.head !== undefined ? { head: result.head } : {}),\n ...(result.tail !== undefined ? { tail: result.tail } : {}),\n ...(result.startLine !== undefined ? { startLine: result.startLine } : {}),\n ...(result.endLine !== undefined ? { endLine: result.endLine } : {}),\n ...(contentHash !== undefined ? { contentHash } : {}),\n resourceUri: buildFileResourceUri(result.path),\n modified: result.modified,\n };\n}\n\n/**\n * The two facts the model cannot act on without: that lines were dropped, and\n * the exact args to get the next chunk. Both used to live only in the structured\n * half, which no longer reaches the model. Each line is `//` prefixed and set\n * off by a blank line so it cannot be read as file bytes — do not drop either\n * guard.\n */\nfunction readTrailer(v: PerPathReadValue): string[] {\n const lines: string[] = [];\n if (v.continuation) {\n lines.push(\n `// truncated: ${v.continuation.hint} Continue: read ${JSON.stringify(v.continuation.args)}`,\n );\n } else if (v.hasMoreLines && v.linesRead !== undefined) {\n // A tail read gets no continuation — there are no args that read *backwards*\n // — so say what was shown rather than leaving the truncation silent.\n lines.push(`// truncated: showing last ${String(v.linesRead)} lines`);\n }\n if (v.contentHash !== undefined) lines.push(`// sha256: ${v.contentHash}`);\n return lines;\n}\n\nasync function readOnePath(\n filePath: string,\n args: ReadFileInput,\n ctx: ToolCtx,\n known?: { validPath: string; stats: Stats },\n): Promise<PerPathReadValue> {\n // Image/audio full read: return a media content block instead of throwing\n // INVALID_INPUT (\"Binary file detected.\"). Line-range reads are\n // text-oriented and stay rejected. `readRaw` enforces the same size cap as\n // the text path (getMaxTextFileSize), so a too-large image surfaces TOO_LARGE\n // rather than blowing memory. svg carries kind:'image' but is XML text —\n // readRaw.isBinary is false for it, so it falls through to the text path.\n const isRangeRead =\n args.head !== undefined ||\n args.tail !== undefined ||\n args.startLine !== undefined ||\n args.endLine !== undefined;\n if (!isRangeRead) {\n const mime = detectMimeType(filePath);\n if (mime.kind === 'image' || mime.kind === 'audio') {\n const raw = await ctx.fs.readRaw(filePath, { signal: ctx.signal });\n if (raw.isBinary) {\n return {\n content: `[binary ${mime.kind} content: ${String(raw.content.length)} bytes; returned as a ${mime.kind} content block]`,\n mimeType: raw.mimeType,\n kind: mime.kind,\n mediaData: raw.content.toString('base64'),\n };\n }\n }\n }\n\n const spec = buildReadSpec(args, ctx.signal);\n const result = known\n ? await readFileWithStats(filePath, known.validPath, known.stats, spec)\n : await ctx.fs.readFile(filePath, spec);\n\n return buildPerPathReadValue(result, {\n includeHash: args.includeHash,\n // The continuation is args the caller pastes back, so it echoes the path\n // they wrote. `result.path` is the resolved absolute one — on Windows that\n // is a backslash-doubling JSON string that also spells out the server root.\n requestedPath: filePath,\n });\n}\n\nexport const READ = defineTool<typeof ReadFileInputSchema, BatchResult<ReadPerPathValue>>({\n name: 'read',\n title: 'Read File',\n description:\n 'Read text files, like cat: whole, or one slice via head, tail, or startLine/endLine. ' +\n 'Images and audio return as media; other binary files are rejected.',\n input: ReadFileInputSchema,\n readOnlyHint: true,\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.NOT_FILE,\n progress: (args) => {\n const isBatch = args.paths !== undefined;\n const name = isBatch ? `${String(args.paths?.length ?? 0)} files` : basename(args.path ?? '');\n if (isBatch) {\n return { label: READ_TOOL_LABEL, subject: name };\n }\n let scope: string | undefined;\n if (args.startLine !== undefined) {\n const end = args.endLine ?? '...';\n scope = `${args.startLine}-${String(end)}`;\n } else if (args.head !== undefined) {\n scope = `head ${args.head}`;\n } else if (args.tail !== undefined) {\n scope = `tail ${args.tail}`;\n }\n return { label: READ_TOOL_LABEL, subject: name, ...(scope ? { scope } : {}) };\n },\n accessPaths: singleOrBatchAccessPaths,\n run: async (args, ctx) => {\n const items: BudgetItem[] =\n args.paths !== undefined\n ? await collectFileBudget(args.paths, READ_MANY_MAX_TOTAL_BYTES, getMaxTextFileSize(), ctx)\n : [{ path: args.path ?? '' }];\n const { results: ordered, summary } = await runOverPaths(\n items,\n ctx,\n ErrorCode.NOT_FILE,\n ({ path, known, skip }) => {\n if (skip) throw skip;\n return readOnePath(path, args, ctx, known);\n },\n );\n\n const resources: ContentBlock[] = [];\n for (const result of ordered) {\n if ('error' in result) continue;\n const v = result.value;\n // Image/audio full read: emit the media content block alongside the\n // structured value. The base64 lives in v.mediaData (set in readOnePath).\n if (v.mediaData && v.mimeType && (v.kind === 'image' || v.kind === 'audio')) {\n resources.push(\n v.kind === 'image'\n ? { type: 'image', data: v.mediaData, mimeType: v.mimeType }\n : { type: 'audio', data: v.mediaData, mimeType: v.mimeType },\n );\n continue;\n }\n if (!v.resourceUri || !v.content) continue;\n // `result.path` is the path as *requested*; `v.resourceUri` was built from\n // the *validated* one. Rebuilding from the request would emit a second URI\n // for the same file whenever the two differ in case — subscribe by one and\n // unsubscribe by the other and the watcher never goes away.\n resources.push(\n buildFileResourceLinkFor(\n v.resourceUri,\n basename(result.path),\n v.mimeType ?? 'application/octet-stream',\n Buffer.byteLength(v.content, 'utf8'),\n v.modified,\n ),\n );\n }\n\n const [firstOrdered] = ordered;\n const body = (v: PerPathReadValue): string => {\n const content = v.content ?? 'read failed';\n const trailer = readTrailer(v);\n if (trailer.length === 0) return content;\n // Exactly one blank line between the bytes and the trailer, whether or not\n // the file ended in a newline.\n return `${content}${content.endsWith('\\n') ? '\\n' : '\\n\\n'}${trailer.join('\\n')}`;\n };\n const text =\n ordered.length === 1 && firstOrdered !== undefined\n ? 'error' in firstOrdered\n ? firstOrdered.error.message\n : body(firstOrdered.value)\n : ordered\n .map((r) => {\n const header = `// ${r.path}`;\n if ('value' in r) return `${header}\\n${body(r.value)}`;\n return `${header}\\n// Error: ${r.error.message}`;\n })\n .join('\\n\\n');\n\n // The bytes ship once. `text` above already carries every file's content\n // verbatim (and image/audio bytes ride the media content block), so\n // repeating them under `results[].value` doubled both the wire payload and\n // the model's token cost for every read. The structured half keeps the\n // metadata a client cannot recover from the text: paths, line counts,\n // hashes, continuations, resourceUri.\n const structuredResults = ordered.map((result) => {\n if ('error' in result) return result;\n const { content: _content, mediaData: _mediaData, ...value } = result.value;\n return { ...result, value };\n });\n\n return {\n structured: { results: structuredResults, summary },\n text,\n isError: isTotalFailure(summary),\n ...(resources.length > 0 ? { resources } : {}),\n };\n },\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"replace-text.d.ts","sourceRoot":"","sources":["../../src/tools/replace-text.ts"],"names":[],"mappings":"AAslBA,eAAO,MAAM,YAAY,mCA+DvB,CAAC"}
1
+ {"version":3,"file":"replace-text.d.ts","sourceRoot":"","sources":["../../src/tools/replace-text.ts"],"names":[],"mappings":"AAikBA,eAAO,MAAM,YAAY,mCA0DvB,CAAC"}
@@ -53,7 +53,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
53
53
  import { Buffer, isUtf8 } from 'node:buffer';
54
54
  import { basename, dirname } from 'node:path';
55
55
  import * as z from 'zod/v4';
56
- import { processEntriesConcurrently, StoppedReasonSchema } from "../core/concurrency.js";
56
+ import { processEntriesConcurrently } from "../core/concurrency.js";
57
57
  import { unifiedPatch } from "../core/diff.js";
58
58
  import { ErrorCode, FsError, Problem } from "../core/errors.js";
59
59
  import { buildWrittenFileMeta } from "../core/file-uri.js";
@@ -61,7 +61,7 @@ import { stoppedEarlyLine, truncateProgressPattern } from "../core/fmt.js";
61
61
  import { globEntries } from "../core/glob.js";
62
62
  import { toPosixRelative } from "../core/path.js";
63
63
  import { readFileBufferWithLimit } from "../core/read.js";
64
- import { defaultFalseBoolean, IncludeHidden, IncludeIgnored, isBlank, MaxDepth, NonNegInt, OperationSummarySchema, OptionalPath, perPathEnvelope, SafeGlobPattern, } from "../core/schema.js";
64
+ import { defaultFalseBoolean, IncludeHidden, IncludeIgnored, isBlank, MaxDepth, OptionalPath, SafeGlobPattern, } from "../core/schema.js";
65
65
  import { compileRegex, execMatches, freeRegex, RE2_MAX_INPUT_BYTES } from "../core/search.js";
66
66
  import { DEFAULT_SEARCH_RESULTS, DEFAULT_SEARCH_TIMEOUT_MS, escapeRegExp, getMaxTextFileSize, MAX_SEARCH_RESULTS, PARALLEL_CONCURRENCY, } from "../core/util.js";
67
67
  import { isTotalFailure } from "./batch.js";
@@ -106,34 +106,6 @@ const SearchAndReplaceInputSchema = z.strictObject({
106
106
  .describe('Maximum number of files to process'),
107
107
  maxDepth: MaxDepth,
108
108
  });
109
- const ReplacePerPathSchema = perPathEnvelope(z.strictObject({ matches: NonNegInt.describe('Replacements applied in this file') }), 'Replacement outcome; present on success');
110
- // The same `{ results, summary }` envelope `read`, `stat`, and `delete` use, so
111
- // a caller learns one shape for every tool that spans many paths. The former
112
- // `filesModified` / `failedFiles` counters are `summary.succeeded` / `.failed`,
113
- // and the old `primaryFile` block is gone — the resource_link content block
114
- // already points at the first modified file.
115
- const SearchAndReplaceOutputSchema = z.strictObject({
116
- results: z
117
- .array(ReplacePerPathSchema)
118
- .describe('Per-file results: modified files, then any that could not be processed'),
119
- summary: OperationSummarySchema,
120
- totalMatches: NonNegInt.describe('Total number of replacements made across all files'),
121
- filesScanned: NonNegInt.describe('Total number of files examined'),
122
- skippedBinary: NonNegInt.optional().describe('Matching files left untouched because they are binary or not valid UTF-8 text'),
123
- resultsTruncated: z
124
- .boolean()
125
- .optional()
126
- .describe('True when the results list holds fewer entries than summary.total: the changed-file or failed-file cap was hit. Trust summary over results.length.'),
127
- diff: z
128
- .string()
129
- .optional()
130
- .describe('Unified diff of all changes (present when returnDiff=true or dryRun=true)'),
131
- diffTruncated: z
132
- .boolean()
133
- .optional()
134
- .describe('True when the diff was cut due to the size limit'),
135
- stoppedReason: StoppedReasonSchema.describe('Why enumeration stopped early: maxResults = match cap reached, maxFiles = file cap reached, timeout = time limit hit or cancelled. Absent when every matching file was enumerated. Marks the sweep incomplete, not the writes; files already dispatched still complete.'),
136
- });
137
109
  const MAX_FAILURES = 20;
138
110
  const REPLACE_CONCURRENCY = Math.min(PARALLEL_CONCURRENCY, 8);
139
111
  const MAX_CHANGED_FILES = 100;
@@ -418,6 +390,7 @@ async function handleSearchAndReplace(args, ctx) {
418
390
  const maxFileSize = getMaxTextFileSize();
419
391
  const { root, singleFile } = await resolveSearchRoot(args.path, ctx.fs);
420
392
  const effectivePattern = args.pattern ?? '**/*';
393
+ const ceiling = ctx.fs.pathGuard.allowedRootContaining(root);
421
394
  // An explicit single-file target bypasses baseNameMatch/exclude/hidden/
422
395
  // gitignore filtering — it should always be processed as the one file named.
423
396
  const entries = singleFile
@@ -427,6 +400,7 @@ async function handleSearchAndReplace(args, ctx) {
427
400
  pattern: effectivePattern,
428
401
  includeHidden: args.includeHidden,
429
402
  skipIgnored: !args.includeIgnored,
403
+ ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),
430
404
  signal: ctx.signal,
431
405
  baseNameMatch: true,
432
406
  onlyFiles: true,
@@ -460,7 +434,7 @@ async function handleSearchAndReplace(args, ctx) {
460
434
  shouldStop: () => summary.totalMatches >= args.maxResults,
461
435
  onEntry: () => {
462
436
  summary.processedFiles++;
463
- ctx.onProgress?.({ current: summary.processedFiles });
437
+ ctx.onProgress({ current: summary.processedFiles });
464
438
  },
465
439
  onError: (entryPath, err) => {
466
440
  summary.failedFiles++;
@@ -477,17 +451,26 @@ async function handleSearchAndReplace(args, ctx) {
477
451
  }
478
452
  if (stoppedReason !== undefined)
479
453
  summary.stoppedReason = stoppedReason;
480
- ctx.onProgress?.({ current: summary.processedFiles });
454
+ ctx.onProgress({ current: summary.processedFiles });
481
455
  if (!args.dryRun && summary.totalMatches > 0) {
482
- ctx.log?.('info', `${summary.filesChanged} file(s) changed, ${summary.totalMatches} match(es)`, 'replace_text');
456
+ ctx.log('info', `${summary.filesChanged} file(s) changed, ${summary.totalMatches} match(es)`, 'replace_text');
483
457
  }
484
458
  const structured = buildSearchAndReplaceStructuredResult(summary, args);
459
+ let primaryModified;
460
+ if (summary.primary && !args.dryRun) {
461
+ try {
462
+ primaryModified = (await ctx.fs.stat(summary.primary.path, { signal: ctx.signal })).stats.mtime.toISOString();
463
+ }
464
+ catch {
465
+ /* result metadata only; the write already succeeded */
466
+ }
467
+ }
485
468
  const link = summary.primary
486
469
  ? buildWrittenFileMeta({
487
470
  validPath: summary.primary.path,
488
471
  content: summary.primary.content,
489
- resourceStore: ctx.resourceStore,
490
472
  dryRun: args.dryRun,
473
+ lastModified: primaryModified,
491
474
  }).resourceLink
492
475
  : undefined;
493
476
  return link ? { structured, link } : { structured };
@@ -499,12 +482,7 @@ export const REPLACE_TEXT = defineTool({
499
482
  'under a directory, optionally filtered by glob. The pattern runs over the whole file: it can span lines, ' +
500
483
  'and ^ and $ anchor to file start and end. edit replaces one unique exact match.',
501
484
  input: SearchAndReplaceInputSchema,
502
- output: SearchAndReplaceOutputSchema,
503
- annotations: {
504
- readOnlyHint: false,
505
- destructiveHint: true,
506
- openWorldHint: false,
507
- },
485
+ readOnlyHint: false,
508
486
  timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,
509
487
  progress: (args) => {
510
488
  const dryLabel = args.dryRun ? ' [dry run]' : '';
@@ -537,9 +515,8 @@ export const REPLACE_TEXT = defineTool({
537
515
  lines.push(`// scan stopped early: hit the ${stop} limit; later files were not scanned. A higher ${stop} reaches them.`);
538
516
  }
539
517
  // A failure's reason lives in `results`, which ships under `_meta`.
540
- for (const r of structured.results.filter((x) => x.error).slice(0, 3)) {
541
- if (r.error)
542
- lines.push(`// ${basename(r.path)}: ${r.error.code} ${r.error.message}`);
518
+ for (const r of structured.results.filter((x) => 'error' in x).slice(0, 3)) {
519
+ lines.push(`// ${basename(r.path)}: ${r.error.code} ${r.error.message}`);
543
520
  }
544
521
  if (structured.diffTruncated) {
545
522
  lines.push(`// diff cut at ${String(MAX_DIFF_SIZE / 1024)} KB: some files' changes are not shown.`);