@j0hanz/filesystem-mcp 1.19.0 → 2.0.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 (301) hide show
  1. package/README.md +333 -838
  2. package/dist/cli-help.d.ts +3 -0
  3. package/dist/cli-help.d.ts.map +1 -0
  4. package/dist/cli-help.js +169 -0
  5. package/dist/cli-help.js.map +1 -0
  6. package/dist/cli.d.ts +28 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +168 -123
  9. package/dist/cli.js.map +1 -0
  10. package/dist/core/concurrency.d.ts +70 -0
  11. package/dist/core/concurrency.d.ts.map +1 -0
  12. package/dist/core/concurrency.js +173 -0
  13. package/dist/core/concurrency.js.map +1 -0
  14. package/dist/core/config.d.ts +19 -0
  15. package/dist/core/config.d.ts.map +1 -0
  16. package/dist/core/config.js +11 -0
  17. package/dist/core/config.js.map +1 -0
  18. package/dist/core/cursor.d.ts +23 -0
  19. package/dist/core/cursor.d.ts.map +1 -0
  20. package/dist/core/cursor.js +62 -0
  21. package/dist/core/cursor.js.map +1 -0
  22. package/dist/core/errors.d.ts +96 -0
  23. package/dist/core/errors.d.ts.map +1 -0
  24. package/dist/core/errors.js +356 -0
  25. package/dist/core/errors.js.map +1 -0
  26. package/dist/core/file-uri.d.ts +24 -0
  27. package/dist/core/file-uri.d.ts.map +1 -0
  28. package/dist/core/file-uri.js +52 -0
  29. package/dist/core/file-uri.js.map +1 -0
  30. package/dist/core/fmt.d.ts +38 -0
  31. package/dist/core/fmt.d.ts.map +1 -0
  32. package/dist/core/fmt.js +109 -0
  33. package/dist/core/fmt.js.map +1 -0
  34. package/dist/core/fs.d.ts +108 -0
  35. package/dist/core/fs.d.ts.map +1 -0
  36. package/dist/core/fs.js +243 -0
  37. package/dist/core/fs.js.map +1 -0
  38. package/dist/core/glob.d.ts +41 -0
  39. package/dist/core/glob.d.ts.map +1 -0
  40. package/dist/core/glob.js +399 -0
  41. package/dist/core/glob.js.map +1 -0
  42. package/dist/core/input-required.d.ts +147 -0
  43. package/dist/core/input-required.d.ts.map +1 -0
  44. package/dist/core/input-required.js +261 -0
  45. package/dist/core/input-required.js.map +1 -0
  46. package/dist/core/mime.d.ts +15 -0
  47. package/dist/core/mime.d.ts.map +1 -0
  48. package/dist/core/mime.js +188 -0
  49. package/dist/core/mime.js.map +1 -0
  50. package/dist/core/observability.d.ts +10 -0
  51. package/dist/core/observability.d.ts.map +1 -0
  52. package/dist/core/observability.js +76 -0
  53. package/dist/core/observability.js.map +1 -0
  54. package/dist/core/page-store.d.ts +41 -0
  55. package/dist/core/page-store.d.ts.map +1 -0
  56. package/dist/core/page-store.js +74 -0
  57. package/dist/core/page-store.js.map +1 -0
  58. package/dist/core/path-completer.d.ts +10 -0
  59. package/dist/core/path-completer.d.ts.map +1 -0
  60. package/dist/core/path-completer.js +227 -0
  61. package/dist/core/path-completer.js.map +1 -0
  62. package/dist/core/path-discovery.d.ts +8 -0
  63. package/dist/core/path-discovery.d.ts.map +1 -0
  64. package/dist/core/path-discovery.js +105 -0
  65. package/dist/core/path-discovery.js.map +1 -0
  66. package/dist/core/path-utils.d.ts +9 -0
  67. package/dist/core/path-utils.d.ts.map +1 -0
  68. package/dist/core/path-utils.js +121 -0
  69. package/dist/core/path-utils.js.map +1 -0
  70. package/dist/core/path.d.ts +148 -0
  71. package/dist/core/path.d.ts.map +1 -0
  72. package/dist/core/path.js +674 -0
  73. package/dist/core/path.js.map +1 -0
  74. package/dist/core/primitives.d.ts +23 -0
  75. package/dist/core/primitives.d.ts.map +1 -0
  76. package/dist/core/primitives.js +54 -0
  77. package/dist/core/primitives.js.map +1 -0
  78. package/dist/core/read.d.ts +74 -0
  79. package/dist/core/read.d.ts.map +1 -0
  80. package/dist/core/read.js +562 -0
  81. package/dist/core/read.js.map +1 -0
  82. package/dist/core/schema.d.ts +139 -0
  83. package/dist/core/schema.d.ts.map +1 -0
  84. package/dist/core/schema.js +345 -0
  85. package/dist/core/schema.js.map +1 -0
  86. package/dist/core/search.d.ts +100 -0
  87. package/dist/core/search.d.ts.map +1 -0
  88. package/dist/core/search.js +248 -0
  89. package/dist/core/search.js.map +1 -0
  90. package/dist/core/sensitive.d.ts +6 -0
  91. package/dist/core/sensitive.d.ts.map +1 -0
  92. package/dist/core/sensitive.js +142 -0
  93. package/dist/core/sensitive.js.map +1 -0
  94. package/dist/core/store.d.ts +57 -0
  95. package/dist/core/store.d.ts.map +1 -0
  96. package/dist/core/store.js +162 -0
  97. package/dist/core/store.js.map +1 -0
  98. package/dist/core/util.d.ts +26 -0
  99. package/dist/core/util.d.ts.map +1 -0
  100. package/dist/core/util.js +66 -0
  101. package/dist/core/util.js.map +1 -0
  102. package/dist/core/watcher-registry.d.ts +68 -0
  103. package/dist/core/watcher-registry.d.ts.map +1 -0
  104. package/dist/core/watcher-registry.js +312 -0
  105. package/dist/core/watcher-registry.js.map +1 -0
  106. package/dist/http-policy.d.ts +121 -0
  107. package/dist/http-policy.d.ts.map +1 -0
  108. package/dist/http-policy.js +340 -0
  109. package/dist/http-policy.js.map +1 -0
  110. package/dist/index.d.ts +1 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +77 -41
  113. package/dist/index.js.map +1 -0
  114. package/dist/instructions.d.ts +12 -0
  115. package/dist/instructions.d.ts.map +1 -0
  116. package/dist/instructions.js +71 -0
  117. package/dist/instructions.js.map +1 -0
  118. package/dist/prompts.d.ts +8 -6
  119. package/dist/prompts.d.ts.map +1 -0
  120. package/dist/prompts.js +74 -168
  121. package/dist/prompts.js.map +1 -0
  122. package/dist/resources.d.ts +73 -9
  123. package/dist/resources.d.ts.map +1 -0
  124. package/dist/resources.js +392 -163
  125. package/dist/resources.js.map +1 -0
  126. package/dist/server.d.ts +62 -1
  127. package/dist/server.d.ts.map +1 -0
  128. package/dist/server.js +139 -1
  129. package/dist/server.js.map +1 -0
  130. package/dist/tools/batch.d.ts +89 -0
  131. package/dist/tools/batch.d.ts.map +1 -0
  132. package/dist/tools/batch.js +157 -0
  133. package/dist/tools/batch.js.map +1 -0
  134. package/dist/tools/create.d.ts +2 -0
  135. package/dist/tools/create.d.ts.map +1 -0
  136. package/dist/tools/create.js +142 -0
  137. package/dist/tools/create.js.map +1 -0
  138. package/dist/tools/define.d.ts +110 -0
  139. package/dist/tools/define.d.ts.map +1 -0
  140. package/dist/tools/define.js +394 -0
  141. package/dist/tools/define.js.map +1 -0
  142. package/dist/tools/delete-file.d.ts +2 -4
  143. package/dist/tools/delete-file.d.ts.map +1 -0
  144. package/dist/tools/delete-file.js +330 -87
  145. package/dist/tools/delete-file.js.map +1 -0
  146. package/dist/tools/diff.d.ts +2 -0
  147. package/dist/tools/diff.d.ts.map +1 -0
  148. package/dist/tools/diff.js +94 -0
  149. package/dist/tools/diff.js.map +1 -0
  150. package/dist/tools/edit.d.ts +2 -0
  151. package/dist/tools/edit.d.ts.map +1 -0
  152. package/dist/tools/edit.js +442 -0
  153. package/dist/tools/edit.js.map +1 -0
  154. package/dist/tools/index.d.ts +26 -0
  155. package/dist/tools/index.d.ts.map +1 -0
  156. package/dist/tools/index.js +52 -0
  157. package/dist/tools/index.js.map +1 -0
  158. package/dist/tools/list.d.ts +2 -0
  159. package/dist/tools/list.d.ts.map +1 -0
  160. package/dist/tools/list.js +291 -0
  161. package/dist/tools/list.js.map +1 -0
  162. package/dist/tools/move.d.ts +2 -0
  163. package/dist/tools/move.d.ts.map +1 -0
  164. package/dist/tools/move.js +321 -0
  165. package/dist/tools/move.js.map +1 -0
  166. package/dist/tools/patch.d.ts +2 -0
  167. package/dist/tools/patch.d.ts.map +1 -0
  168. package/dist/tools/patch.js +155 -0
  169. package/dist/tools/patch.js.map +1 -0
  170. package/dist/tools/progress.d.ts +52 -0
  171. package/dist/tools/progress.d.ts.map +1 -0
  172. package/dist/tools/progress.js +172 -0
  173. package/dist/tools/progress.js.map +1 -0
  174. package/dist/tools/read.d.ts +2 -4
  175. package/dist/tools/read.d.ts.map +1 -0
  176. package/dist/tools/read.js +398 -152
  177. package/dist/tools/read.js.map +1 -0
  178. package/dist/tools/replace-in-files.d.ts +2 -4
  179. package/dist/tools/replace-in-files.d.ts.map +1 -0
  180. package/dist/tools/replace-in-files.js +435 -264
  181. package/dist/tools/replace-in-files.js.map +1 -0
  182. package/dist/tools/roots.d.ts +2 -4
  183. package/dist/tools/roots.d.ts.map +1 -0
  184. package/dist/tools/roots.js +29 -52
  185. package/dist/tools/roots.js.map +1 -0
  186. package/dist/tools/search-content.d.ts +2 -4
  187. package/dist/tools/search-content.d.ts.map +1 -0
  188. package/dist/tools/search-content.js +262 -228
  189. package/dist/tools/search-content.js.map +1 -0
  190. package/dist/tools/search-files.d.ts +2 -4
  191. package/dist/tools/search-files.d.ts.map +1 -0
  192. package/dist/tools/search-files.js +162 -161
  193. package/dist/tools/search-files.js.map +1 -0
  194. package/dist/tools/stat.d.ts +2 -4
  195. package/dist/tools/stat.d.ts.map +1 -0
  196. package/dist/tools/stat.js +202 -62
  197. package/dist/tools/stat.js.map +1 -0
  198. package/dist/transport/http.d.ts +5 -0
  199. package/dist/transport/http.d.ts.map +1 -0
  200. package/dist/transport/http.js +276 -0
  201. package/dist/transport/http.js.map +1 -0
  202. package/dist/transport/shared.d.ts +45 -0
  203. package/dist/transport/shared.d.ts.map +1 -0
  204. package/dist/transport/shared.js +65 -0
  205. package/dist/transport/shared.js.map +1 -0
  206. package/dist/transport/stdio.d.ts +32 -0
  207. package/dist/transport/stdio.d.ts.map +1 -0
  208. package/dist/transport/stdio.js +279 -0
  209. package/dist/transport/stdio.js.map +1 -0
  210. package/dist/transport.d.ts +5 -0
  211. package/dist/transport.d.ts.map +1 -0
  212. package/dist/transport.js +4 -0
  213. package/dist/transport.js.map +1 -0
  214. package/package.json +37 -46
  215. package/dist/assets/logo.svg +0 -35
  216. package/dist/completions.d.ts +0 -2
  217. package/dist/completions.js +0 -572
  218. package/dist/config.d.ts +0 -119
  219. package/dist/config.js +0 -31
  220. package/dist/lib/abort.d.ts +0 -7
  221. package/dist/lib/abort.js +0 -81
  222. package/dist/lib/constants.d.ts +0 -42
  223. package/dist/lib/constants.js +0 -313
  224. package/dist/lib/errors.d.ts +0 -28
  225. package/dist/lib/errors.js +0 -301
  226. package/dist/lib/file-operations/core.d.ts +0 -53
  227. package/dist/lib/file-operations/core.js +0 -144
  228. package/dist/lib/file-operations/metadata.d.ts +0 -77
  229. package/dist/lib/file-operations/metadata.js +0 -878
  230. package/dist/lib/file-operations/search.d.ts +0 -46
  231. package/dist/lib/file-operations/search.js +0 -1194
  232. package/dist/lib/file-operations/traversal.d.ts +0 -40
  233. package/dist/lib/file-operations/traversal.js +0 -310
  234. package/dist/lib/fs-helpers.d.ts +0 -46
  235. package/dist/lib/fs-helpers.js +0 -606
  236. package/dist/lib/globs.d.ts +0 -2
  237. package/dist/lib/globs.js +0 -19
  238. package/dist/lib/logger.d.ts +0 -27
  239. package/dist/lib/logger.js +0 -91
  240. package/dist/lib/observability.d.ts +0 -33
  241. package/dist/lib/observability.js +0 -373
  242. package/dist/lib/paths.d.ts +0 -35
  243. package/dist/lib/paths.js +0 -634
  244. package/dist/lib/resource-store.d.ts +0 -28
  245. package/dist/lib/resource-store.js +0 -195
  246. package/dist/lib/utils.d.ts +0 -7
  247. package/dist/lib/utils.js +0 -37
  248. package/dist/lib/zod-codecs.d.ts +0 -2
  249. package/dist/lib/zod-codecs.js +0 -18
  250. package/dist/pkg-info.d.ts +0 -7
  251. package/dist/pkg-info.js +0 -9
  252. package/dist/resources/generated-instructions.d.ts +0 -1
  253. package/dist/resources/generated-instructions.js +0 -105
  254. package/dist/resources/tool-catalog.d.ts +0 -2
  255. package/dist/resources/tool-catalog.js +0 -78
  256. package/dist/resources/tool-info.d.ts +0 -9
  257. package/dist/resources/tool-info.js +0 -206
  258. package/dist/resources/workflows.d.ts +0 -1
  259. package/dist/resources/workflows.js +0 -72
  260. package/dist/schemas.d.ts +0 -505
  261. package/dist/schemas.js +0 -676
  262. package/dist/server/bootstrap.d.ts +0 -6
  263. package/dist/server/bootstrap.js +0 -563
  264. package/dist/server/event-store.d.ts +0 -18
  265. package/dist/server/event-store.js +0 -71
  266. package/dist/server/roots-manager.d.ts +0 -28
  267. package/dist/server/roots-manager.js +0 -227
  268. package/dist/server/task-store.d.ts +0 -10
  269. package/dist/server/task-store.js +0 -91
  270. package/dist/tools/apply-patch.d.ts +0 -4
  271. package/dist/tools/apply-patch.js +0 -235
  272. package/dist/tools/calculate-hash.d.ts +0 -4
  273. package/dist/tools/calculate-hash.js +0 -187
  274. package/dist/tools/contract.d.ts +0 -50
  275. package/dist/tools/contract.js +0 -1
  276. package/dist/tools/create-directory.d.ts +0 -4
  277. package/dist/tools/create-directory.js +0 -68
  278. package/dist/tools/diff-files.d.ts +0 -4
  279. package/dist/tools/diff-files.js +0 -136
  280. package/dist/tools/edit-file.d.ts +0 -4
  281. package/dist/tools/edit-file.js +0 -256
  282. package/dist/tools/icons.d.ts +0 -15
  283. package/dist/tools/icons.js +0 -24
  284. package/dist/tools/list-directory.d.ts +0 -4
  285. package/dist/tools/list-directory.js +0 -218
  286. package/dist/tools/move-file.d.ts +0 -4
  287. package/dist/tools/move-file.js +0 -178
  288. package/dist/tools/read-multiple.d.ts +0 -4
  289. package/dist/tools/read-multiple.js +0 -207
  290. package/dist/tools/shared.d.ts +0 -172
  291. package/dist/tools/shared.js +0 -599
  292. package/dist/tools/stat-many.d.ts +0 -4
  293. package/dist/tools/stat-many.js +0 -104
  294. package/dist/tools/task-support.d.ts +0 -29
  295. package/dist/tools/task-support.js +0 -472
  296. package/dist/tools/tree.d.ts +0 -4
  297. package/dist/tools/tree.js +0 -102
  298. package/dist/tools/write-file.d.ts +0 -4
  299. package/dist/tools/write-file.js +0 -60
  300. package/dist/tools.d.ts +0 -5
  301. package/dist/tools.js +0 -54
@@ -1,121 +1,121 @@
1
- import { relative } from 'node:path';
2
- import RE2 from 're2';
3
- import { DEFAULT_EXCLUDE_PATTERNS } from '../lib/constants.js';
4
- import { classifyError, ErrorCode, formatUnknownErrorMessage, McpError, } from '../lib/errors.js';
5
- import { searchContent, } from '../lib/file-operations/search.js';
6
- import { formatOperationSummary } from '../config.js';
7
- import { SearchContentInputSchema, SearchContentOutputSchema, } from '../schemas.js';
8
- import { SEARCH_ICONS } from './icons.js';
9
- import { buildResourceLink, buildToolErrorResponse, buildToolResponse, createToolProgressSession, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, resolveFinalProgressCurrent, resolvePathOrRoot, truncateProgressPattern, } from './shared.js';
10
- import { registerStandardTool, reportTaskStatus } from './task-support.js';
1
+ import { stat } from 'node:fs/promises';
2
+ import { basename, dirname } from 'node:path';
3
+ import * as z from 'zod/v4';
4
+ import { SearchStoppedReasonSchema } from '../core/concurrency.js';
5
+ import { createFirstPage, readNextPage } from '../core/cursor.js';
6
+ import { ErrorCode, FsError } from '../core/errors.js';
7
+ import { formatCount, truncateProgressPattern } from '../core/fmt.js';
8
+ import { DEFAULT_EXCLUDE_PATTERNS } from '../core/glob.js';
9
+ import { toPosixRelative } from '../core/path.js';
10
+ import { escapeRegexLiteral } from '../core/primitives.js';
11
+ import { CursorSchema, defaultFalseBoolean, includeHiddenField, includeIgnoredField, isBlank, maxDepthField, NextCursorSchema, NonNegInt, OptionalPath, PositiveInt, SafeGlobPattern, } from '../core/schema.js';
12
+ import { compileRegex, freeRegex, searchContent } from '../core/search.js';
13
+ import { putJsonResource } from '../core/store.js';
14
+ import { DEFAULT_SEARCH_CONTENT_RESULTS, DEFAULT_SEARCH_TIMEOUT_MS, MAX_SEARCH_RESULTS, parseEnvInt, } from '../core/util.js';
15
+ import { defineTool } from './define.js';
11
16
  /**
12
17
  * Configuration constants for the Search Content tool.
13
18
  */
14
19
  const CONFIG = {
15
- MAX_INLINE_MATCHES: parseInt(process.env['FS_CONTEXT_MAX_INLINE_MATCHES'] ?? '', 10) || 50,
16
- COMPLETION_LABELS: {
17
- timeout: 'timeout',
18
- maxResults: 'max results',
19
- maxFiles: 'max files',
20
- },
20
+ MAX_INLINE_MATCHES: parseEnvInt('FS_MAX_INLINE_MATCHES', 50, 1, 10_000),
21
21
  };
22
- const TRUTHY_SUMMARY_FIELDS = [
23
- 'filesMatched',
24
- 'skippedTooLarge',
25
- 'skippedBinary',
26
- 'skippedInaccessible',
27
- ];
28
- function buildStructuredSummaryFields(summary) {
29
- const result = {};
30
- for (const key of TRUTHY_SUMMARY_FIELDS) {
31
- const value = summary[key];
32
- if (value) {
33
- result[key] = value;
34
- }
35
- }
36
- if (summary.truncated) {
37
- result.truncated = true;
38
- }
39
- if (summary.stoppedReason) {
40
- result.stoppedReason = summary.stoppedReason;
41
- }
42
- return result;
43
- }
44
- function buildCompletionSuffix(count, filesMatched, scope, stoppedReason) {
45
- if (count === 0)
46
- return `No matches in ${scope}`;
47
- const matchWord = count === 1 ? 'match' : 'matches';
48
- const fileWord = filesMatched === 1 ? 'file' : 'files';
49
- const reasonSuffix = stoppedReason !== undefined
50
- ? ` [${CONFIG.COMPLETION_LABELS[stoppedReason]}]`
51
- : '';
52
- return `${count} ${matchWord} in ${filesMatched} ${fileWord}${reasonSuffix}`;
53
- }
54
22
  function buildSearchPreviewState(payloads) {
55
23
  const needsExternalize = payloads.length > CONFIG.MAX_INLINE_MATCHES;
56
- const visibleCount = needsExternalize
57
- ? CONFIG.MAX_INLINE_MATCHES
58
- : payloads.length;
24
+ const visibleCount = needsExternalize ? CONFIG.MAX_INLINE_MATCHES : payloads.length;
59
25
  return {
60
26
  needsExternalize,
61
27
  visiblePayloads: payloads.slice(0, visibleCount),
62
- heading: buildHeading(payloads.length, visibleCount),
63
28
  };
64
29
  }
65
- export const SEARCH_CONTENT_TOOL = {
66
- name: 'grep',
67
- title: 'Search Content',
68
- description: 'Search file contents for text (grep-like). Returns matching lines. ' +
69
- 'Scope with `filePattern` (e.g. `**/*.ts`) to reduce noise. ' +
70
- '`includeHidden=true` for dotfiles.',
71
- inputSchema: SearchContentInputSchema,
72
- outputSchema: SearchContentOutputSchema,
73
- annotations: READ_ONLY_TOOL_ANNOTATIONS,
74
- icons: SEARCH_ICONS,
75
- nuances: [
76
- 'Inline results capped at 50 matches; full results via `resourceUri`.',
77
- ],
78
- gotchas: [
79
- 'Skips binary/oversized files silently — verify with `stat` if no matches.',
80
- ],
81
- taskSupport: 'optional',
82
- };
83
- function buildHeading(totalMatches, visibleMatches) {
84
- if (visibleMatches >= totalMatches) {
85
- return `Found ${totalMatches}:`;
86
- }
87
- return `Found ${totalMatches} (showing first ${visibleMatches}):`;
88
- }
89
- function buildSearchText(heading, matches, summary) {
90
- if (matches.length === 0)
91
- return 'No matches';
92
- const text = buildMatchList(heading, matches);
93
- if (!summary)
94
- return text;
95
- const summaryOpts = {
96
- truncated: summary.truncated,
97
- ...(summary.truncated
98
- ? { truncatedReason: resolveTruncatedReason(summary) }
99
- : {}),
100
- };
101
- return text + formatOperationSummary(summaryOpts);
30
+ const GrepInputSchema = z.strictObject({
31
+ path: OptionalPath.describe('File to search, or directory to search under (default: the whole first allowed root). Naming a file searches that file alone: pattern is ignored and hidden/ignored filtering does not apply.'),
32
+ pattern: SafeGlobPattern.optional().describe('Glob to restrict search to specific file types (e.g. **/*.ts); default: all text files'),
33
+ searchPattern: z
34
+ .string()
35
+ .min(1)
36
+ .max(10000)
37
+ .refine((val) => !isBlank(val), {
38
+ message: 'searchPattern cannot be empty or whitespace-only',
39
+ })
40
+ .describe('Exact literal text or RE2 regex pattern to search for in file contents. When isRegex=true, uses RE2 syntax (no lookahead, lookbehind, or backreferences). Cannot be empty or whitespace-only.')
41
+ .meta({ examples: ['TODO', 'function\\s+(\\w+)', 'import.*from'] }),
42
+ isRegex: defaultFalseBoolean('Treat searchPattern as a regex (default: literal text match)'),
43
+ includeHidden: includeHiddenField(),
44
+ includeIgnored: includeIgnoredField(),
45
+ caseSensitive: defaultFalseBoolean('Enable case-sensitive matching (default: case-insensitive)'),
46
+ maxResults: z
47
+ .uint32()
48
+ .min(1)
49
+ .max(MAX_SEARCH_RESULTS)
50
+ .optional()
51
+ .default(DEFAULT_SEARCH_CONTENT_RESULTS)
52
+ .describe('Maximum number of matching lines to return per page'),
53
+ maxDepth: maxDepthField(),
54
+ cursor: CursorSchema,
55
+ });
56
+ const GrepOutputSchema = z.strictObject({
57
+ matches: z
58
+ .array(z.strictObject({
59
+ file: z.string().describe('File path relative to the search root'),
60
+ line: PositiveInt.describe('1-indexed line number of the match'),
61
+ column: NonNegInt.optional().describe('0-indexed column offset of the match start'),
62
+ content: z.string().describe('Full text of the matching line'),
63
+ matchCount: NonNegInt.optional().describe('Number of pattern occurrences on this line'),
64
+ }))
65
+ .describe('Flat list of matches sorted by file path then line number'),
66
+ totalMatches: NonNegInt.optional().describe("Total matching lines, one per entry in matches (not per occurrence); see that entry's matchCount for occurrences on a line."),
67
+ filesMatched: NonNegInt.optional().describe('Number of files containing at least one match'),
68
+ filesScanned: NonNegInt.optional().describe('Total number of files examined'),
69
+ skippedInaccessible: NonNegInt.optional().describe('Files skipped unread due to permission or access errors'),
70
+ skippedTooLarge: NonNegInt.optional().describe('Files skipped unread because they exceed the text-file size limit; raise the limit or narrow pattern if a match was expected in one'),
71
+ truncated: z
72
+ .boolean()
73
+ .optional()
74
+ .describe('True when the match list was cut due to maxResults or timeout'),
75
+ stoppedReason: SearchStoppedReasonSchema.describe('Why the search ended early: maxResults = result cap reached, timeout = time limit hit or the request was cancelled. Absent when the scan ran to completion.'),
76
+ resourceUri: z
77
+ .string()
78
+ .optional()
79
+ .describe('URI to the full match list in the resource store (present when matches exceed the inline limit)'),
80
+ nextCursor: NextCursorSchema,
81
+ });
82
+ function buildSearchMatchDetail(totalMatches, filesMatched) {
83
+ const matchDetail = formatCount(totalMatches, 'match', 'matches');
84
+ if (filesMatched <= 0)
85
+ return matchDetail;
86
+ return `${matchDetail} · ${formatCount(filesMatched, 'file', 'files')}`;
102
87
  }
103
- function buildSearchStructured(summary, matches) {
88
+ function searchContentOutput(matches, metadata) {
104
89
  return {
105
- ok: true,
106
90
  matches,
107
- totalMatches: summary.matches,
108
- filesScanned: summary.filesScanned,
109
- ...buildStructuredSummaryFields(summary),
91
+ totalMatches: metadata.totalMatches,
92
+ filesScanned: metadata.filesScanned,
93
+ ...(metadata.filesMatched ? { filesMatched: metadata.filesMatched } : {}),
94
+ ...(metadata.truncated ? { truncated: true } : {}),
95
+ ...(metadata.stoppedReason !== undefined ? { stoppedReason: metadata.stoppedReason } : {}),
96
+ ...(metadata.skippedInaccessible ? { skippedInaccessible: metadata.skippedInaccessible } : {}),
97
+ ...(metadata.skippedTooLarge ? { skippedTooLarge: metadata.skippedTooLarge } : {}),
110
98
  };
111
99
  }
100
+ function findColumnOffset(content, context) {
101
+ if (context.matcher) {
102
+ context.matcher.lastIndex = 0;
103
+ const match = context.matcher.exec(content);
104
+ return match ? match.index : undefined;
105
+ }
106
+ if (context.caseSensitive) {
107
+ const idx = content.indexOf(context.pattern);
108
+ return idx >= 0 ? idx : undefined;
109
+ }
110
+ return undefined;
111
+ }
112
112
  function buildSortedPayloads(result, context) {
113
113
  const relativeByFile = new Map();
114
114
  const getRelativeFile = (file) => {
115
115
  const cached = relativeByFile.get(file);
116
116
  if (cached !== undefined)
117
117
  return cached;
118
- const rel = relative(result.basePath, file);
118
+ const rel = toPosixRelative(result.basePath, file);
119
119
  relativeByFile.set(file, rel);
120
120
  return rel;
121
121
  };
@@ -128,10 +128,6 @@ function buildSortedPayloads(result, context) {
128
128
  ...(column !== undefined ? { column } : {}),
129
129
  content: match.content,
130
130
  matchCount: match.matchCount,
131
- ...(match.contextBefore
132
- ? { contextBefore: [...match.contextBefore] }
133
- : {}),
134
- ...(match.contextAfter ? { contextAfter: [...match.contextAfter] } : {}),
135
131
  };
136
132
  return { payload, originalIndex };
137
133
  });
@@ -145,154 +141,192 @@ function buildSortedPayloads(result, context) {
145
141
  });
146
142
  return payloads.map((p) => p.payload);
147
143
  }
148
- function buildMatchList(heading, matches) {
149
- if (matches.length === 0)
150
- return heading;
151
- const parts = [heading];
152
- for (const match of matches) {
153
- parts.push(`\n ${match.file}:${String(match.line).padStart(4)}: ${match.content}`);
154
- }
155
- return parts.join('');
156
- }
157
- function resolveTruncatedReason(summary) {
158
- if (summary.stoppedReason === 'timeout')
159
- return 'timeout';
160
- if (summary.stoppedReason === 'maxFiles') {
161
- return `max files (${summary.filesScanned})`;
162
- }
163
- return `max results (${summary.matches})`;
164
- }
165
- function findColumnOffset(content, context) {
166
- try {
167
- if (context.matcher) {
168
- context.matcher.lastIndex = 0;
169
- const match = context.matcher.exec(content);
170
- return match ? match.index : undefined;
171
- }
172
- if (context.caseSensitive) {
173
- const idx = content.indexOf(context.pattern);
174
- return idx >= 0 ? idx : undefined;
175
- }
176
- // Case-insensitive literal search
177
- const idx = content.toLowerCase().indexOf(context.foldedPattern ?? '');
178
- return idx >= 0 ? idx : undefined;
179
- }
180
- catch {
181
- return undefined;
182
- }
183
- }
184
- async function executeSearch(args, basePath, signal, onProgress) {
185
- const excludePatterns = args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS;
186
- const options = {
144
+ function buildSearchContentOptions(args, signal) {
145
+ return {
187
146
  includeHidden: args.includeHidden,
188
- excludePatterns,
189
- filePattern: args.filePattern,
147
+ excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,
148
+ filePattern: args.pattern ?? '**/*',
190
149
  caseSensitive: args.caseSensitive,
191
- wholeWord: args.wholeWord,
192
- contextLines: args.contextLines,
150
+ isRegex: args.isRegex,
193
151
  maxResults: args.maxResults,
194
- isLiteral: !args.isRegex,
152
+ respectGitignore: !args.includeIgnored,
153
+ ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),
195
154
  ...(signal ? { signal } : {}),
196
- ...(onProgress ? { onProgress } : {}),
197
155
  };
198
- try {
199
- return await searchContent(basePath, args.pattern, options);
200
- }
201
- catch (error) {
202
- if (error instanceof Error && /regular expression/i.test(error.message)) {
203
- throw new McpError(ErrorCode.INVALID_PATTERN, `Invalid regex pattern: ${formatUnknownErrorMessage(error)} (RE2: no lookahead/lookbehind/backrefs)`);
204
- }
205
- throw error;
206
- }
207
156
  }
208
157
  function createSearchMatcher(args) {
209
- if (!args.isRegex)
210
- return undefined;
211
- try {
212
- const flags = args.caseSensitive ? '' : 'i';
213
- return new RE2(args.pattern, flags);
158
+ if (args.isRegex) {
159
+ return compileRegex(args.searchPattern, { caseSensitive: args.caseSensitive });
214
160
  }
215
- catch (error) {
216
- throw new McpError(ErrorCode.INVALID_PATTERN, `Invalid regex pattern: ${formatUnknownErrorMessage(error)} (RE2: no lookahead/lookbehind/backrefs)`);
161
+ if (!args.caseSensitive) {
162
+ return compileRegex(escapeRegexLiteral(args.searchPattern), { caseSensitive: false });
217
163
  }
164
+ return undefined;
218
165
  }
219
166
  function createSearchContext(args, matcher) {
220
167
  return {
221
- pattern: args.pattern,
168
+ pattern: args.searchPattern,
222
169
  caseSensitive: args.caseSensitive,
223
170
  ...(matcher ? { matcher } : {}),
224
- ...(!args.isRegex && !args.caseSensitive
225
- ? { foldedPattern: args.pattern.toLowerCase() }
226
- : {}),
227
171
  };
228
172
  }
229
- async function handleSearchContent(args, signal, resourceStore, onProgress) {
230
- const basePath = resolvePathOrRoot(args.path);
231
- const regexMatcher = createSearchMatcher(args);
232
- const result = await executeSearch(args, basePath, signal, onProgress);
173
+ function buildExternalizedResponse(fullStructured, preview, resourceStore, searchPattern) {
174
+ const { entry, link } = putJsonResource(resourceStore, `'${searchPattern}' matches`, fullStructured);
175
+ const structuredForResponse = {
176
+ ...fullStructured,
177
+ resourceUri: entry.uri,
178
+ };
179
+ if (preview.needsExternalize) {
180
+ structuredForResponse.matches = preview.visiblePayloads;
181
+ structuredForResponse.truncated = true;
182
+ }
183
+ return { structured: structuredForResponse, link };
184
+ }
185
+ function sortedPayloads(result, args, regexMatcher) {
233
186
  const searchContext = createSearchContext(args, regexMatcher);
234
- const matchPayloads = buildSortedPayloads(result, searchContext);
235
- const fullStructured = buildSearchStructured(result.summary, matchPayloads);
236
- const preview = buildSearchPreviewState(matchPayloads);
187
+ return buildSortedPayloads(result, searchContext);
188
+ }
189
+ function finalizeSearchOutput(fullStructured, preview, resourceStore, searchPattern) {
237
190
  if (resourceStore && preview.needsExternalize) {
238
- const previewStructured = {
239
- ...fullStructured,
240
- matches: preview.visiblePayloads,
241
- truncated: true,
242
- };
243
- const entry = resourceStore.putText({
244
- name: 'grep:matches',
245
- mimeType: 'application/json',
246
- text: JSON.stringify(fullStructured),
247
- });
248
- previewStructured.resourceUri = entry.uri;
249
- const text = buildSearchText(preview.heading, preview.visiblePayloads);
250
- return buildToolResponse(text, previewStructured, [
251
- buildResourceLink({
252
- uri: entry.uri,
253
- name: entry.name,
254
- mimeType: entry.mimeType,
255
- description: 'Full grep results as JSON (structuredContent)',
256
- expiresAt: entry.expiresAt,
257
- }),
258
- ]);
191
+ return buildExternalizedResponse(fullStructured, preview, resourceStore, searchPattern ?? '');
192
+ }
193
+ if (preview.needsExternalize) {
194
+ fullStructured.matches = preview.visiblePayloads;
195
+ fullStructured.truncated = true;
259
196
  }
260
- const text = buildSearchText(preview.heading, preview.visiblePayloads, result.summary);
261
- return buildToolResponse(text, fullStructured);
197
+ return { structured: fullStructured };
262
198
  }
263
- export function registerSearchContentTool(server, options = {}) {
264
- const handler = (args, ctx) => executeToolWithDiagnostics({
265
- toolName: 'grep',
266
- ctx,
267
- outputSchema: SearchContentOutputSchema,
268
- context: { path: args.path ?? '.' },
269
- run: async (signal) => {
270
- const { pattern, filePattern: scope } = args;
271
- const progressLabel = `${SEARCH_CONTENT_TOOL.title}: ${truncateProgressPattern(pattern)}`;
272
- const progress = createToolProgressSession(ctx, progressLabel);
273
- const progressWithMessage = ({ current, total, }) => {
274
- progress.update({
275
- current,
276
- ...(total !== undefined ? { total } : {}),
277
- message: `${progressLabel} [${current} files]`,
278
- });
279
- void reportTaskStatus(`${progressLabel} ${current} files`);
280
- };
281
- try {
282
- const result = await handleSearchContent(args, signal, options.resourceStore, progressWithMessage);
283
- const sc = result.structuredContent;
284
- const { totalMatches = 0, filesMatched = 0, stoppedReason } = sc;
285
- const suffix = buildCompletionSuffix(totalMatches, filesMatched, scope, stoppedReason);
286
- const finalCurrent = resolveFinalProgressCurrent(progress, (sc.filesScanned ?? 0) + 1);
287
- progress.complete(`${progressLabel} ${suffix}`, finalCurrent);
288
- return result;
289
- }
290
- catch (error) {
291
- progress.fail(`${progressLabel} • ${classifyError(error)}`);
292
- throw error;
293
- }
294
- },
295
- onError: (error) => buildToolErrorResponse(error, ErrorCode.UNKNOWN, args.path ?? '.'),
199
+ /**
200
+ * `path` is documented as "file to search, or directory to search under", but
201
+ * the scan itself only walks directories. A file path is therefore rewritten
202
+ * into the scan it means: its parent as the base, its own name as the glob.
203
+ * Naming a file is an explicit request for that file, so hidden/ignored
204
+ * filtering is lifted for it — otherwise `search_text` on a path the caller can
205
+ * see returned nothing.
206
+ */
207
+ async function resolveSearchScope(args, ctx) {
208
+ const requested = ctx.fs.pathGuard.resolvePathOrRoot(args.path);
209
+ // One resolution, one stat: validateExistingDirectory would redo both.
210
+ const resolved = await ctx.fs.pathGuard.validateExistingPath(requested);
211
+ const stats = await stat(resolved);
212
+ if (!stats.isFile()) {
213
+ if (!stats.isDirectory()) {
214
+ throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);
215
+ }
216
+ return { basePath: resolved, args };
217
+ }
218
+ return {
219
+ basePath: dirname(resolved),
220
+ // ponytail: the name goes through as a glob, so a file whose name contains
221
+ // glob metacharacters (`[id].ts`) matches as a pattern rather than
222
+ // literally. Escape it here if that ever bites.
223
+ args: { ...args, pattern: basename(resolved), includeHidden: true, includeIgnored: true },
224
+ };
225
+ }
226
+ function searchContentQueryKey(args, requestedPath) {
227
+ return JSON.stringify({
228
+ method: 'search_text',
229
+ path: requestedPath,
230
+ pattern: args.pattern,
231
+ searchPattern: args.searchPattern,
232
+ isRegex: args.isRegex,
233
+ includeHidden: args.includeHidden,
234
+ includeIgnored: args.includeIgnored,
235
+ caseSensitive: args.caseSensitive,
236
+ maxDepth: args.maxDepth,
296
237
  });
297
- registerStandardTool(server, SEARCH_CONTENT_TOOL, handler, options);
298
238
  }
239
+ async function handleSearchContent(args, ctx) {
240
+ const requestedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);
241
+ const queryKey = searchContentQueryKey(args, requestedPath);
242
+ if (args.cursor !== undefined) {
243
+ const paged = readNextPage({
244
+ store: ctx.pageStore,
245
+ queryKey,
246
+ cursor: args.cursor,
247
+ pageSize: args.maxResults,
248
+ });
249
+ const fullStructured = searchContentOutput([...paged.page], paged.metadata);
250
+ if (paged.nextCursor !== undefined)
251
+ fullStructured.nextCursor = paged.nextCursor;
252
+ const preview = buildSearchPreviewState([...paged.page]);
253
+ return finalizeSearchOutput(fullStructured, preview, ctx.resourceStore, args.searchPattern);
254
+ }
255
+ const { basePath, args: scoped } = await resolveSearchScope(args, ctx);
256
+ const regexMatcher = createSearchMatcher(scoped);
257
+ const result = await searchContent(basePath, scoped.searchPattern, buildSearchContentOptions({ ...scoped, maxResults: MAX_SEARCH_RESULTS }, ctx.signal), ctx.fs.pathGuard);
258
+ // regexMatcher holds wasm memory re2-wasm never reclaims on its own.
259
+ let allMatchPayloads;
260
+ try {
261
+ allMatchPayloads = sortedPayloads(result, scoped, regexMatcher);
262
+ }
263
+ finally {
264
+ freeRegex(regexMatcher);
265
+ }
266
+ const metadata = {
267
+ totalMatches: result.summary.matchingLines,
268
+ filesScanned: result.summary.filesScanned,
269
+ ...(result.summary.filesMatched ? { filesMatched: result.summary.filesMatched } : {}),
270
+ truncated: result.summary.truncated,
271
+ ...(result.summary.stoppedReason !== undefined && result.summary.stoppedReason !== 'maxFiles'
272
+ ? { stoppedReason: result.summary.stoppedReason }
273
+ : {}),
274
+ ...(result.summary.skippedInaccessible
275
+ ? { skippedInaccessible: result.summary.skippedInaccessible }
276
+ : {}),
277
+ ...(result.summary.skippedTooLarge ? { skippedTooLarge: result.summary.skippedTooLarge } : {}),
278
+ };
279
+ const paged = createFirstPage({
280
+ store: ctx.pageStore,
281
+ queryKey,
282
+ items: allMatchPayloads,
283
+ metadata,
284
+ pageSize: scoped.maxResults,
285
+ });
286
+ const fullStructured = searchContentOutput([...paged.page], metadata);
287
+ if (paged.nextCursor !== undefined)
288
+ fullStructured.nextCursor = paged.nextCursor;
289
+ const preview = buildSearchPreviewState([...paged.page]);
290
+ const { structured, link } = finalizeSearchOutput(fullStructured, preview, ctx.resourceStore, args.searchPattern);
291
+ return {
292
+ structured,
293
+ ...(link !== undefined ? { link } : {}),
294
+ };
295
+ }
296
+ export const SEARCH_CONTENT = defineTool({
297
+ name: 'search_text',
298
+ title: 'Search Content',
299
+ description: 'Search file contents by text or regex (grep-style). Returns matching lines with file path, ' +
300
+ '1-indexed line number and 0-indexed column offset. ' +
301
+ 'Scope to specific file types with pattern (e.g. **/*.ts). ' +
302
+ 'Set includeHidden=true to include dotfiles. Use find_files to search by filename instead.',
303
+ input: GrepInputSchema,
304
+ output: GrepOutputSchema,
305
+ annotations: {
306
+ readOnlyHint: true,
307
+ idempotentHint: true,
308
+ destructiveHint: false,
309
+ openWorldHint: false,
310
+ },
311
+ timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,
312
+ defaultErrorCode: ErrorCode.UNKNOWN,
313
+ progress: (args) => ({
314
+ label: 'Search',
315
+ subject: truncateProgressPattern(args.searchPattern),
316
+ }),
317
+ progressDone: (_args, result) => ({
318
+ detail: buildSearchMatchDetail(result.totalMatches ?? 0, result.filesMatched ?? 0),
319
+ }),
320
+ accessPaths: (args) => (args.path ? [args.path] : []),
321
+ run: async (args, ctx) => {
322
+ const { structured, link } = await handleSearchContent(args, ctx);
323
+ const text = structured.matches.length > 0
324
+ ? structured.matches.map((m) => `${m.file}:${String(m.line)}: ${m.content}`).join('\n')
325
+ : `No matches for '${args.searchPattern}'`;
326
+ if (link) {
327
+ return { structured, text, resources: [link] };
328
+ }
329
+ return { structured, text };
330
+ },
331
+ });
332
+ //# sourceMappingURL=search-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-content.js","sourceRoot":"","sources":["../../src/tools/search-content.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { SearchStoppedReasonSchema } from '../core/concurrency.js';\nimport { createFirstPage, readNextPage } from '../core/cursor.js';\nimport { ErrorCode, FsError } from '../core/errors.js';\nimport { formatCount, truncateProgressPattern } from '../core/fmt.js';\nimport { DEFAULT_EXCLUDE_PATTERNS } from '../core/glob.js';\nimport { toPosixRelative } from '../core/path.js';\nimport { escapeRegexLiteral } from '../core/primitives.js';\nimport {\n CursorSchema,\n defaultFalseBoolean,\n includeHiddenField,\n includeIgnoredField,\n isBlank,\n maxDepthField,\n NextCursorSchema,\n NonNegInt,\n OptionalPath,\n PositiveInt,\n SafeGlobPattern,\n} from '../core/schema.js';\nimport type { Regex, SearchContentOptions } from '../core/search.js';\nimport { compileRegex, freeRegex, searchContent } from '../core/search.js';\nimport type { ResourceStore } from '../core/store.js';\nimport { putJsonResource } from '../core/store.js';\nimport {\n DEFAULT_SEARCH_CONTENT_RESULTS,\n DEFAULT_SEARCH_TIMEOUT_MS,\n MAX_SEARCH_RESULTS,\n parseEnvInt,\n} from '../core/util.js';\nimport { defineTool, type ToolCtx } from './define.js';\n\n/**\n * Configuration constants for the Search Content tool.\n */\nconst CONFIG = {\n MAX_INLINE_MATCHES: parseEnvInt('FS_MAX_INLINE_MATCHES', 50, 1, 10_000),\n} as const;\n\n// Type Definitions\ntype SearchInput = z.infer<typeof GrepInputSchema>;\ntype SearchOutput = z.infer<typeof GrepOutputSchema>;\ntype SearchMatchPayload = NonNullable<SearchOutput['matches']>[number];\ntype SearchResultValue = Awaited<ReturnType<typeof searchContent>>;\n\ninterface SearchPreviewState {\n needsExternalize: boolean;\n visiblePayloads: SearchMatchPayload[];\n}\n\ninterface SearchContext {\n pattern: string;\n matcher?: Regex;\n caseSensitive: boolean;\n}\n\ninterface SearchContentPageMetadata {\n readonly totalMatches: number;\n readonly filesScanned: number;\n readonly filesMatched?: number;\n readonly truncated: boolean;\n readonly stoppedReason?: SearchOutput['stoppedReason'];\n readonly skippedInaccessible?: number;\n readonly skippedTooLarge?: number;\n}\n\nfunction buildSearchPreviewState(payloads: SearchMatchPayload[]): SearchPreviewState {\n const needsExternalize = payloads.length > CONFIG.MAX_INLINE_MATCHES;\n const visibleCount = needsExternalize ? CONFIG.MAX_INLINE_MATCHES : payloads.length;\n\n return {\n needsExternalize,\n visiblePayloads: payloads.slice(0, visibleCount),\n };\n}\n\nconst GrepInputSchema = z.strictObject({\n path: OptionalPath.describe(\n 'File to search, or directory to search under (default: the whole first allowed root). Naming a file searches that file alone: pattern is ignored and hidden/ignored filtering does not apply.',\n ),\n pattern: SafeGlobPattern.optional().describe(\n 'Glob to restrict search to specific file types (e.g. **/*.ts); default: all text files',\n ),\n searchPattern: z\n .string()\n .min(1)\n .max(10000)\n .refine((val) => !isBlank(val), {\n message: 'searchPattern cannot be empty or whitespace-only',\n })\n .describe(\n 'Exact literal text or RE2 regex pattern to search for in file contents. When isRegex=true, uses RE2 syntax (no lookahead, lookbehind, or backreferences). Cannot be empty or whitespace-only.',\n )\n .meta({ examples: ['TODO', 'function\\\\s+(\\\\w+)', 'import.*from'] }),\n isRegex: defaultFalseBoolean('Treat searchPattern as a regex (default: literal text match)'),\n includeHidden: includeHiddenField(),\n includeIgnored: includeIgnoredField(),\n caseSensitive: defaultFalseBoolean('Enable case-sensitive matching (default: case-insensitive)'),\n maxResults: z\n .uint32()\n .min(1)\n .max(MAX_SEARCH_RESULTS)\n .optional()\n .default(DEFAULT_SEARCH_CONTENT_RESULTS)\n .describe('Maximum number of matching lines to return per page'),\n maxDepth: maxDepthField(),\n cursor: CursorSchema,\n});\n\nconst GrepOutputSchema = z.strictObject({\n matches: z\n .array(\n z.strictObject({\n file: z.string().describe('File path relative to the search root'),\n line: PositiveInt.describe('1-indexed line number of the match'),\n column: NonNegInt.optional().describe('0-indexed column offset of the match start'),\n content: z.string().describe('Full text of the matching line'),\n matchCount: NonNegInt.optional().describe('Number of pattern occurrences on this line'),\n }),\n )\n .describe('Flat list of matches sorted by file path then line number'),\n totalMatches: NonNegInt.optional().describe(\n \"Total matching lines, one per entry in matches (not per occurrence); see that entry's matchCount for occurrences on a line.\",\n ),\n filesMatched: NonNegInt.optional().describe('Number of files containing at least one match'),\n filesScanned: NonNegInt.optional().describe('Total number of files examined'),\n skippedInaccessible: NonNegInt.optional().describe(\n 'Files skipped unread due to permission or access errors',\n ),\n skippedTooLarge: NonNegInt.optional().describe(\n 'Files skipped unread because they exceed the text-file size limit; raise the limit or narrow pattern if a match was expected in one',\n ),\n truncated: z\n .boolean()\n .optional()\n .describe('True when the match list was cut due to maxResults or timeout'),\n stoppedReason: SearchStoppedReasonSchema.describe(\n 'Why the search ended early: maxResults = result cap reached, timeout = time limit hit or the request was cancelled. Absent when the scan ran to completion.',\n ),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'URI to the full match list in the resource store (present when matches exceed the inline limit)',\n ),\n nextCursor: NextCursorSchema,\n});\n\nfunction buildSearchMatchDetail(totalMatches: number, filesMatched: number): string {\n const matchDetail = formatCount(totalMatches, 'match', 'matches');\n if (filesMatched <= 0) return matchDetail;\n return `${matchDetail} · ${formatCount(filesMatched, 'file', 'files')}`;\n}\n\nfunction searchContentOutput(\n matches: SearchMatchPayload[],\n metadata: SearchContentPageMetadata,\n): SearchOutput {\n return {\n matches,\n totalMatches: metadata.totalMatches,\n filesScanned: metadata.filesScanned,\n ...(metadata.filesMatched ? { filesMatched: metadata.filesMatched } : {}),\n ...(metadata.truncated ? { truncated: true } : {}),\n ...(metadata.stoppedReason !== undefined ? { stoppedReason: metadata.stoppedReason } : {}),\n ...(metadata.skippedInaccessible ? { skippedInaccessible: metadata.skippedInaccessible } : {}),\n ...(metadata.skippedTooLarge ? { skippedTooLarge: metadata.skippedTooLarge } : {}),\n };\n}\n\nfunction findColumnOffset(content: string, context: SearchContext): number | undefined {\n if (context.matcher) {\n context.matcher.lastIndex = 0;\n const match = context.matcher.exec(content);\n return match ? match.index : undefined;\n }\n if (context.caseSensitive) {\n const idx = content.indexOf(context.pattern);\n return idx >= 0 ? idx : undefined;\n }\n return undefined;\n}\n\nfunction buildSortedPayloads(\n result: SearchResultValue,\n context: SearchContext,\n): SearchMatchPayload[] {\n const relativeByFile = new Map<string, string>();\n\n const getRelativeFile = (file: string): string => {\n const cached = relativeByFile.get(file);\n if (cached !== undefined) return cached;\n\n const rel = toPosixRelative(result.basePath, file);\n relativeByFile.set(file, rel);\n return rel;\n };\n\n const payloads = result.matches.map((match, originalIndex) => {\n const relFile = getRelativeFile(match.file);\n const column = findColumnOffset(match.content, context);\n\n const payload: SearchMatchPayload = {\n file: relFile,\n line: match.line,\n ...(column !== undefined ? { column } : {}),\n content: match.content,\n matchCount: match.matchCount,\n };\n\n return { payload, originalIndex };\n });\n\n payloads.sort((left, right) => {\n const fileCompare = left.payload.file.localeCompare(right.payload.file);\n if (fileCompare !== 0) return fileCompare;\n if (left.payload.line !== right.payload.line) return left.payload.line - right.payload.line;\n return left.originalIndex - right.originalIndex;\n });\n\n return payloads.map((p) => p.payload);\n}\n\nfunction buildSearchContentOptions(args: SearchInput, signal?: AbortSignal): SearchContentOptions {\n return {\n includeHidden: args.includeHidden,\n excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,\n filePattern: args.pattern ?? '**/*',\n caseSensitive: args.caseSensitive,\n isRegex: args.isRegex,\n maxResults: args.maxResults,\n respectGitignore: !args.includeIgnored,\n ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),\n ...(signal ? { signal } : {}),\n };\n}\n\nfunction createSearchMatcher(args: SearchInput): Regex | undefined {\n if (args.isRegex) {\n return compileRegex(args.searchPattern, { caseSensitive: args.caseSensitive });\n }\n if (!args.caseSensitive) {\n return compileRegex(escapeRegexLiteral(args.searchPattern), { caseSensitive: false });\n }\n return undefined;\n}\n\nfunction createSearchContext(args: SearchInput, matcher: Regex | undefined): SearchContext {\n return {\n pattern: args.searchPattern,\n caseSensitive: args.caseSensitive,\n ...(matcher ? { matcher } : {}),\n };\n}\n\nfunction buildExternalizedResponse(\n fullStructured: SearchOutput,\n preview: SearchPreviewState,\n resourceStore: ResourceStore,\n searchPattern: string,\n): { structured: SearchOutput; link: ReturnType<typeof putJsonResource>['link'] } {\n const { entry, link } = putJsonResource(\n resourceStore,\n `'${searchPattern}' matches`,\n fullStructured,\n );\n\n const structuredForResponse: SearchOutput = {\n ...fullStructured,\n resourceUri: entry.uri,\n };\n\n if (preview.needsExternalize) {\n structuredForResponse.matches = preview.visiblePayloads;\n structuredForResponse.truncated = true;\n }\n\n return { structured: structuredForResponse, link };\n}\n\nfunction sortedPayloads(\n result: SearchResultValue,\n args: SearchInput,\n regexMatcher: Regex | undefined,\n): SearchMatchPayload[] {\n const searchContext = createSearchContext(args, regexMatcher);\n return buildSortedPayloads(result, searchContext);\n}\n\nfunction finalizeSearchOutput(\n fullStructured: SearchOutput,\n preview: SearchPreviewState,\n resourceStore?: ResourceStore,\n searchPattern?: string,\n): { structured: SearchOutput; link?: ReturnType<typeof putJsonResource>['link'] } {\n if (resourceStore && preview.needsExternalize) {\n return buildExternalizedResponse(fullStructured, preview, resourceStore, searchPattern ?? '');\n }\n\n if (preview.needsExternalize) {\n fullStructured.matches = preview.visiblePayloads;\n fullStructured.truncated = true;\n }\n\n return { structured: fullStructured };\n}\n\n/**\n * `path` is documented as \"file to search, or directory to search under\", but\n * the scan itself only walks directories. A file path is therefore rewritten\n * into the scan it means: its parent as the base, its own name as the glob.\n * Naming a file is an explicit request for that file, so hidden/ignored\n * filtering is lifted for it — otherwise `search_text` on a path the caller can\n * see returned nothing.\n */\nasync function resolveSearchScope(\n args: SearchInput,\n ctx: ToolCtx,\n): Promise<{ basePath: string; args: SearchInput }> {\n const requested = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n // One resolution, one stat: validateExistingDirectory would redo both.\n const resolved = await ctx.fs.pathGuard.validateExistingPath(requested);\n const stats = await stat(resolved);\n if (!stats.isFile()) {\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);\n }\n return { basePath: resolved, args };\n }\n return {\n basePath: dirname(resolved),\n // ponytail: the name goes through as a glob, so a file whose name contains\n // glob metacharacters (`[id].ts`) matches as a pattern rather than\n // literally. Escape it here if that ever bites.\n args: { ...args, pattern: basename(resolved), includeHidden: true, includeIgnored: true },\n };\n}\n\nfunction searchContentQueryKey(args: SearchInput, requestedPath: string): string {\n return JSON.stringify({\n method: 'search_text',\n path: requestedPath,\n pattern: args.pattern,\n searchPattern: args.searchPattern,\n isRegex: args.isRegex,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\n caseSensitive: args.caseSensitive,\n maxDepth: args.maxDepth,\n });\n}\n\nasync function handleSearchContent(\n args: SearchInput,\n ctx: ToolCtx,\n): Promise<{\n structured: SearchOutput;\n link?: ReturnType<typeof putJsonResource>['link'];\n}> {\n const requestedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n const queryKey = searchContentQueryKey(args, requestedPath);\n\n if (args.cursor !== undefined) {\n const paged = readNextPage<SearchMatchPayload, SearchContentPageMetadata>({\n store: ctx.pageStore,\n queryKey,\n cursor: args.cursor,\n pageSize: args.maxResults,\n });\n const fullStructured = searchContentOutput([...paged.page], paged.metadata);\n if (paged.nextCursor !== undefined) fullStructured.nextCursor = paged.nextCursor;\n const preview = buildSearchPreviewState([...paged.page]);\n return finalizeSearchOutput(fullStructured, preview, ctx.resourceStore, args.searchPattern);\n }\n\n const { basePath, args: scoped } = await resolveSearchScope(args, ctx);\n const regexMatcher = createSearchMatcher(scoped);\n\n const result = await searchContent(\n basePath,\n scoped.searchPattern,\n buildSearchContentOptions({ ...scoped, maxResults: MAX_SEARCH_RESULTS }, ctx.signal),\n ctx.fs.pathGuard,\n );\n\n // regexMatcher holds wasm memory re2-wasm never reclaims on its own.\n let allMatchPayloads: SearchMatchPayload[];\n try {\n allMatchPayloads = sortedPayloads(result, scoped, regexMatcher);\n } finally {\n freeRegex(regexMatcher);\n }\n const metadata: SearchContentPageMetadata = {\n totalMatches: result.summary.matchingLines,\n filesScanned: result.summary.filesScanned,\n ...(result.summary.filesMatched ? { filesMatched: result.summary.filesMatched } : {}),\n truncated: result.summary.truncated,\n ...(result.summary.stoppedReason !== undefined && result.summary.stoppedReason !== 'maxFiles'\n ? { stoppedReason: result.summary.stoppedReason }\n : {}),\n ...(result.summary.skippedInaccessible\n ? { skippedInaccessible: result.summary.skippedInaccessible }\n : {}),\n ...(result.summary.skippedTooLarge ? { skippedTooLarge: result.summary.skippedTooLarge } : {}),\n };\n const paged = createFirstPage<SearchMatchPayload, SearchContentPageMetadata>({\n store: ctx.pageStore,\n queryKey,\n items: allMatchPayloads,\n metadata,\n pageSize: scoped.maxResults,\n });\n const fullStructured = searchContentOutput([...paged.page], metadata);\n if (paged.nextCursor !== undefined) fullStructured.nextCursor = paged.nextCursor;\n const preview = buildSearchPreviewState([...paged.page]);\n const { structured, link } = finalizeSearchOutput(\n fullStructured,\n preview,\n ctx.resourceStore,\n args.searchPattern,\n );\n\n return {\n structured,\n ...(link !== undefined ? { link } : {}),\n };\n}\n\nexport const SEARCH_CONTENT = defineTool({\n name: 'search_text',\n title: 'Search Content',\n description:\n 'Search file contents by text or regex (grep-style). Returns matching lines with file path, ' +\n '1-indexed line number and 0-indexed column offset. ' +\n 'Scope to specific file types with pattern (e.g. **/*.ts). ' +\n 'Set includeHidden=true to include dotfiles. Use find_files to search by filename instead.',\n input: GrepInputSchema,\n output: GrepOutputSchema,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.UNKNOWN,\n progress: (args) => ({\n label: 'Search',\n subject: truncateProgressPattern(args.searchPattern),\n }),\n progressDone: (_args, result) => ({\n detail: buildSearchMatchDetail(result.totalMatches ?? 0, result.filesMatched ?? 0),\n }),\n accessPaths: (args) => (args.path ? [args.path] : []),\n run: async (args, ctx) => {\n const { structured, link } = await handleSearchContent(args, ctx);\n const text =\n structured.matches.length > 0\n ? structured.matches.map((m) => `${m.file}:${String(m.line)}: ${m.content}`).join('\\n')\n : `No matches for '${args.searchPattern}'`;\n if (link) {\n return { structured, text, resources: [link] };\n }\n return { structured, text };\n },\n});\n"]}
@@ -1,4 +1,2 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
3
- export declare const SEARCH_FILES_TOOL: ToolContract;
4
- export declare function registerSearchFilesTool(server: McpServer, options?: ToolRegistrationOptions): void;
1
+ export declare const SEARCH_FILES: import("./define.js").DefinedTool;
2
+ //# sourceMappingURL=search-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-files.d.ts","sourceRoot":"","sources":["../../src/tools/search-files.ts"],"names":[],"mappings":"AAuNA,eAAO,MAAM,YAAY,mCAoCvB,CAAC"}