@j0hanz/filesystem-mcp 2.1.8 → 2.3.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 (149) hide show
  1. package/README.md +41 -29
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +10 -2
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +10 -5
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/concurrency.d.ts.map +1 -1
  9. package/dist/core/concurrency.js +21 -6
  10. package/dist/core/concurrency.js.map +1 -1
  11. package/dist/core/config.d.ts +2 -0
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js.map +1 -1
  14. package/dist/core/cursor.d.ts +11 -0
  15. package/dist/core/cursor.d.ts.map +1 -1
  16. package/dist/core/cursor.js +10 -0
  17. package/dist/core/cursor.js.map +1 -1
  18. package/dist/core/errors.d.ts +6 -9
  19. package/dist/core/errors.d.ts.map +1 -1
  20. package/dist/core/errors.js +58 -102
  21. package/dist/core/errors.js.map +1 -1
  22. package/dist/core/file-uri.d.ts +5 -1
  23. package/dist/core/file-uri.d.ts.map +1 -1
  24. package/dist/core/file-uri.js +6 -2
  25. package/dist/core/file-uri.js.map +1 -1
  26. package/dist/core/fmt.d.ts +0 -1
  27. package/dist/core/fmt.d.ts.map +1 -1
  28. package/dist/core/fmt.js +0 -1
  29. package/dist/core/fmt.js.map +1 -1
  30. package/dist/core/fs.d.ts +31 -1
  31. package/dist/core/fs.d.ts.map +1 -1
  32. package/dist/core/fs.js +142 -2
  33. package/dist/core/fs.js.map +1 -1
  34. package/dist/core/glob.d.ts +4 -13
  35. package/dist/core/glob.d.ts.map +1 -1
  36. package/dist/core/glob.js +14 -14
  37. package/dist/core/glob.js.map +1 -1
  38. package/dist/core/input-required.d.ts +16 -17
  39. package/dist/core/input-required.d.ts.map +1 -1
  40. package/dist/core/input-required.js +42 -47
  41. package/dist/core/input-required.js.map +1 -1
  42. package/dist/core/observability.d.ts +9 -0
  43. package/dist/core/observability.d.ts.map +1 -1
  44. package/dist/core/observability.js +13 -1
  45. package/dist/core/observability.js.map +1 -1
  46. package/dist/core/page-store.d.ts +4 -3
  47. package/dist/core/page-store.d.ts.map +1 -1
  48. package/dist/core/page-store.js +4 -3
  49. package/dist/core/page-store.js.map +1 -1
  50. package/dist/core/primitives.d.ts +7 -0
  51. package/dist/core/primitives.d.ts.map +1 -1
  52. package/dist/core/primitives.js +15 -8
  53. package/dist/core/primitives.js.map +1 -1
  54. package/dist/core/search.d.ts +8 -4
  55. package/dist/core/search.d.ts.map +1 -1
  56. package/dist/core/search.js +23 -24
  57. package/dist/core/search.js.map +1 -1
  58. package/dist/core/sensitive.d.ts +4 -2
  59. package/dist/core/sensitive.d.ts.map +1 -1
  60. package/dist/core/sensitive.js +261 -43
  61. package/dist/core/sensitive.js.map +1 -1
  62. package/dist/core/util.d.ts.map +1 -1
  63. package/dist/core/util.js +2 -11
  64. package/dist/core/util.js.map +1 -1
  65. package/dist/core/watcher-registry.d.ts.map +1 -1
  66. package/dist/core/watcher-registry.js +5 -1
  67. package/dist/core/watcher-registry.js.map +1 -1
  68. package/dist/instructions.js +11 -11
  69. package/dist/instructions.js.map +1 -1
  70. package/dist/resources.d.ts.map +1 -1
  71. package/dist/resources.js +13 -4
  72. package/dist/resources.js.map +1 -1
  73. package/dist/server.d.ts +1 -9
  74. package/dist/server.d.ts.map +1 -1
  75. package/dist/server.js +22 -27
  76. package/dist/server.js.map +1 -1
  77. package/dist/tools/create.d.ts.map +1 -1
  78. package/dist/tools/create.js +163 -14
  79. package/dist/tools/create.js.map +1 -1
  80. package/dist/tools/define.d.ts +6 -1
  81. package/dist/tools/define.d.ts.map +1 -1
  82. package/dist/tools/define.js +29 -9
  83. package/dist/tools/define.js.map +1 -1
  84. package/dist/tools/delete.d.ts +2 -0
  85. package/dist/tools/delete.d.ts.map +1 -0
  86. package/dist/tools/{delete-file.js → delete.js} +12 -15
  87. package/dist/tools/delete.js.map +1 -0
  88. package/dist/tools/edit.d.ts.map +1 -1
  89. package/dist/tools/edit.js +77 -69
  90. package/dist/tools/edit.js.map +1 -1
  91. package/dist/tools/find-files.d.ts +2 -0
  92. package/dist/tools/find-files.d.ts.map +1 -0
  93. package/dist/tools/{search-files.js → find-files.js} +14 -19
  94. package/dist/tools/find-files.js.map +1 -0
  95. package/dist/tools/index.d.ts +7 -6
  96. package/dist/tools/index.d.ts.map +1 -1
  97. package/dist/tools/index.js +16 -15
  98. package/dist/tools/index.js.map +1 -1
  99. package/dist/tools/list-roots.d.ts +2 -0
  100. package/dist/tools/list-roots.d.ts.map +1 -0
  101. package/dist/tools/{roots.js → list-roots.js} +2 -2
  102. package/dist/tools/list-roots.js.map +1 -0
  103. package/dist/tools/list.d.ts.map +1 -1
  104. package/dist/tools/list.js +28 -32
  105. package/dist/tools/list.js.map +1 -1
  106. package/dist/tools/move.d.ts.map +1 -1
  107. package/dist/tools/move.js +17 -36
  108. package/dist/tools/move.js.map +1 -1
  109. package/dist/tools/patch.js +3 -3
  110. package/dist/tools/patch.js.map +1 -1
  111. package/dist/tools/read.d.ts +1 -1
  112. package/dist/tools/read.d.ts.map +1 -1
  113. package/dist/tools/read.js +1 -1
  114. package/dist/tools/read.js.map +1 -1
  115. package/dist/tools/replace-text.d.ts +2 -0
  116. package/dist/tools/replace-text.d.ts.map +1 -0
  117. package/dist/tools/{replace-in-files.js → replace-text.js} +5 -5
  118. package/dist/tools/replace-text.js.map +1 -0
  119. package/dist/tools/search-text.d.ts +2 -0
  120. package/dist/tools/search-text.d.ts.map +1 -0
  121. package/dist/tools/{search-content.js → search-text.js} +71 -13
  122. package/dist/tools/search-text.js.map +1 -0
  123. package/dist/tools/stat.d.ts +1 -1
  124. package/dist/tools/stat.d.ts.map +1 -1
  125. package/dist/tools/stat.js +1 -1
  126. package/dist/tools/stat.js.map +1 -1
  127. package/dist/transport/http.d.ts.map +1 -1
  128. package/dist/transport/http.js +5 -1
  129. package/dist/transport/http.js.map +1 -1
  130. package/dist/transport/stdio.d.ts +1 -0
  131. package/dist/transport/stdio.d.ts.map +1 -1
  132. package/dist/transport/stdio.js +1 -0
  133. package/dist/transport/stdio.js.map +1 -1
  134. package/package.json +6 -9
  135. package/dist/tools/delete-file.d.ts +0 -2
  136. package/dist/tools/delete-file.d.ts.map +0 -1
  137. package/dist/tools/delete-file.js.map +0 -1
  138. package/dist/tools/replace-in-files.d.ts +0 -2
  139. package/dist/tools/replace-in-files.d.ts.map +0 -1
  140. package/dist/tools/replace-in-files.js.map +0 -1
  141. package/dist/tools/roots.d.ts +0 -2
  142. package/dist/tools/roots.d.ts.map +0 -1
  143. package/dist/tools/roots.js.map +0 -1
  144. package/dist/tools/search-content.d.ts +0 -2
  145. package/dist/tools/search-content.d.ts.map +0 -1
  146. package/dist/tools/search-content.js.map +0 -1
  147. package/dist/tools/search-files.d.ts +0 -2
  148. package/dist/tools/search-files.d.ts.map +0 -1
  149. package/dist/tools/search-files.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-text.js","sourceRoot":"","sources":["../../src/tools/search-text.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 { pageQueryKey, paginate } from '../core/cursor.js';\nimport { ErrorCode, FsError } from '../core/errors.js';\nimport { formatCount, pageTrailer, truncateProgressPattern } from '../core/fmt.js';\nimport { Logger } from '../core/observability.js';\nimport { toPosixRelative } from '../core/path.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 { SearchContentOptions } from '../core/search.js';\nimport { searchContent } from '../core/search.js';\nimport type { JsonResourceResult } 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} from '../core/util.js';\nimport { defineTool, type ToolCtx } from './define.js';\n\n/**\n * `FS_MAX_INLINE_MATCHES` used to cap how many matches a page showed inline\n * and, past it, push the rest into the resource store. `maxResults` is the one\n * page size now, and the full list is externalized whenever a response is\n * incomplete, so the variable does nothing. It is still read so an operator who\n * set it hears why it stopped mattering; the read goes at the next major.\n */\nif (process.env['FS_MAX_INLINE_MATCHES'] !== undefined) {\n Logger.warn(\n 'FS_MAX_INLINE_MATCHES is deprecated and ignored: maxResults sets the search_text page size. It will be removed in the next major release.',\n );\n}\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 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\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 context: z\n .uint32()\n .max(10)\n .optional()\n .default(0)\n .describe(\n 'Lines of context to return either side of each match, like grep -C (default: 0, max: 10)',\n ),\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 before: z\n .array(z.string())\n .optional()\n .describe('Lines immediately before the match; present when context > 0'),\n after: z\n .array(z.string())\n .optional()\n .describe('Lines immediately after the match; present when context > 0'),\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(\n 'True when the search engine cut the match list — the hard result cap or the time limit. Paging is not truncation: a set that spans pages has nextCursor instead.',\n ),\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; first page only, whenever the response is incomplete — more pages follow, or the engine cut the search',\n ),\n nextCursor: NextCursorSchema,\n});\n\nfunction matchRow(m: SearchMatchPayload): string {\n return `${m.file}:${String(m.line)}: ${m.content}`;\n}\n\n/**\n * grep -C layout: `file-line- text` for a context line, `--` between groups\n * that are not adjacent. Every match's window lands in a per-file line map\n * where a match wins over context, so a line two windows share prints once,\n * and as a match when it is one. The maps iterate in line order without a\n * sort: matches arrive sorted, each window is contiguous, and setting an\n * existing key keeps its slot. Without context the rows are the bare match\n * lines, byte-identical to before the option existed.\n */\nfunction renderRows(matches: readonly SearchMatchPayload[], withContext: boolean): string[] {\n if (!withContext) return matches.map(matchRow);\n const byFile = new Map<string, Map<number, string>>();\n for (const m of matches) {\n const lines = byFile.get(m.file) ?? new Map<number, string>();\n byFile.set(m.file, lines);\n const setContext = (n: number, text: string): void => {\n if (!lines.has(n)) lines.set(n, `${m.file}-${String(n)}- ${text}`);\n };\n const before = m.before ?? [];\n before.forEach((text, k) => {\n setContext(m.line - before.length + k, text);\n });\n lines.set(m.line, matchRow(m));\n (m.after ?? []).forEach((text, k) => {\n setContext(m.line + 1 + k, text);\n });\n }\n const rows: string[] = [];\n for (const lines of byFile.values()) {\n let prev: number | undefined;\n for (const [n, row] of lines) {\n if (prev === undefined ? rows.length > 0 : n > prev + 1) rows.push('--');\n rows.push(row);\n prev = n;\n }\n }\n return rows;\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: readonly SearchMatchPayload[],\n metadata: SearchContentPageMetadata,\n nextCursor: string | undefined,\n resourceUri: string | undefined,\n): SearchOutput {\n return {\n matches: [...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 ...(resourceUri !== undefined ? { resourceUri } : {}),\n ...(nextCursor !== undefined ? { nextCursor } : {}),\n };\n}\n\nfunction buildSortedPayloads(result: SearchResultValue): 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): SearchMatchPayload => ({\n file: getRelativeFile(match.file),\n line: match.line,\n column: match.column,\n content: match.content,\n matchCount: match.matchCount,\n ...(match.before !== undefined ? { before: match.before } : {}),\n ...(match.after !== undefined ? { after: match.after } : {}),\n }));\n\n payloads.sort((l, r) => l.file.localeCompare(r.file) || l.line - r.line);\n return payloads;\n}\n\nfunction buildSearchContentOptions(args: SearchInput, signal?: AbortSignal): SearchContentOptions {\n return {\n includeHidden: args.includeHidden,\n filePattern: args.pattern ?? '**/*',\n caseSensitive: args.caseSensitive,\n isRegex: args.isRegex,\n maxResults: args.maxResults,\n skipIgnored: !args.includeIgnored,\n context: args.context,\n ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),\n ...(signal ? { signal } : {}),\n };\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\nasync function handleSearchContent(\n args: SearchInput,\n ctx: ToolCtx,\n): Promise<{\n structured: SearchOutput;\n offset: number;\n total: number;\n link?: ReturnType<typeof putJsonResource>['link'];\n}> {\n const requestedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n const queryKey = pageQueryKey({\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 context: args.context,\n });\n const { resourceStore } = ctx;\n\n const paged = await paginate<SearchMatchPayload, SearchContentPageMetadata, JsonResourceResult>({\n store: ctx.pageStore,\n queryKey,\n cursor: args.cursor,\n pageSize: args.maxResults,\n produce: async () => {\n const { basePath, args: scoped } = await resolveSearchScope(args, ctx);\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 const items = buildSortedPayloads(result);\n return {\n items,\n metadata: {\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\n ? { stoppedReason: result.summary.stoppedReason }\n : {}),\n ...(result.summary.skippedInaccessible\n ? { skippedInaccessible: result.summary.skippedInaccessible }\n : {}),\n ...(result.summary.skippedTooLarge\n ? { skippedTooLarge: result.summary.skippedTooLarge }\n : {}),\n },\n truncated: result.summary.truncated,\n };\n },\n externalize: resourceStore\n ? (matches, metadata) =>\n putJsonResource(\n resourceStore,\n `'${args.searchPattern}' matches`,\n searchContentOutput(matches, metadata, undefined, undefined),\n )\n : undefined,\n });\n\n return {\n structured: searchContentOutput(\n paged.page,\n paged.metadata,\n paged.nextCursor,\n paged.resource?.entry.uri,\n ),\n offset: paged.offset,\n total: paged.metadata.totalMatches,\n ...(paged.resource ? { link: paged.resource.link } : {}),\n };\n}\n\nexport const SEARCH_TEXT = 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 'Set context=N to also return N lines either side of each match (grep -C). ' +\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, offset, total, link } = await handleSearchContent(args, ctx);\n const rows = renderRows(structured.matches, args.context > 0);\n const body = rows.length > 0 ? rows.join('\\n') : `No matches for '${args.searchPattern}'`;\n const text =\n body +\n pageTrailer({\n offset,\n shown: rows.length,\n total,\n noun: 'matches',\n tool: 'search_text',\n nextCursor: structured.nextCursor,\n stoppedReason: structured.stoppedReason,\n });\n if (link) {\n return { structured, text, resources: [link] };\n }\n return { structured, text };\n },\n});\n"]}
@@ -1,2 +1,2 @@
1
- export declare const GET_FILE_INFO: import("./define.js").DefinedTool;
1
+ export declare const STAT: import("./define.js").DefinedTool;
2
2
  //# sourceMappingURL=stat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stat.d.ts","sourceRoot":"","sources":["../../src/tools/stat.ts"],"names":[],"mappings":"AAkKA,eAAO,MAAM,aAAa,mCAiExB,CAAC"}
1
+ {"version":3,"file":"stat.d.ts","sourceRoot":"","sources":["../../src/tools/stat.ts"],"names":[],"mappings":"AAkKA,eAAO,MAAM,IAAI,mCAiEf,CAAC"}
@@ -111,7 +111,7 @@ function classifyTypeCounts(results) {
111
111
  }
112
112
  return { fileCount, dirCount };
113
113
  }
114
- export const GET_FILE_INFO = defineTool({
114
+ export const STAT = defineTool({
115
115
  name: 'stat',
116
116
  title: 'Get File Info',
117
117
  description: 'Get metadata for one or more files or directories: size, type, permissions, MIME type, timestamps, and tokenEstimate. ' +
@@ -1 +1 @@
1
- {"version":3,"file":"stat.js","sourceRoot":"","sources":["../../src/tools/stat.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { parse } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { ErrorCode, rethrowIfAborted } from '../core/errors.js';\nimport type { GuardedFileSystem, Stats } from '../core/fs.js';\nimport { isHidden } from '../core/fs.js';\nimport { detectMimeType } from '../core/mime.js';\nimport { resolveEntryType } from '../core/primitives.js';\nimport {\n FileInfoSchema,\n NonNegInt,\n OperationSummarySchema,\n PerFileErrorSchema,\n singleOrBatchAccessPaths,\n singleOrBatchPathsInput,\n} from '../core/schema.js';\nimport { putJsonResource } from '../core/store.js';\nimport { DEFAULT_SEARCH_TIMEOUT_MS } from '../core/util.js';\nimport type { PerPathResult } from './batch.js';\nimport { isTotalFailure, runOverPaths } from './batch.js';\nimport type { ToolCtx } from './define.js';\nimport { defineTool } from './define.js';\n\ntype FileInfo = z.infer<typeof FileInfoSchema>;\n\nconst StatInputSchema = singleOrBatchPathsInput({});\n\nconst StatPerPathSchema = z.strictObject({\n path: z.string().describe('Requested path'),\n value: FileInfoSchema.optional().describe('File metadata; present on success'),\n error: PerFileErrorSchema.optional().describe('Error details; present on failure'),\n});\n\nconst StatOutputSchema = z.strictObject({\n results: z\n .array(StatPerPathSchema)\n .describe('Per-path metadata results ordered to match the input paths'),\n summary: OperationSummarySchema,\n fileCount: NonNegInt.optional().describe('Number of regular files in the results'),\n dirCount: NonNegInt.optional().describe('Number of directories in the results'),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'URI to aggregated stats.json in the resource store (present when resource store is available)',\n ),\n});\n\nfunction getPermissions(mode: number): string {\n let out = '';\n for (const shift of [6, 3, 0]) {\n const bits = (mode >> shift) & 0b111;\n out += (bits & 0b100 ? 'r' : '-') + (bits & 0b010 ? 'w' : '-') + (bits & 0b001 ? 'x' : '-');\n }\n return out;\n}\n\nfunction buildFileInfoResult(\n name: string,\n requestedPath: string,\n isSymlink: boolean,\n stats: Stats,\n mimeType: string | undefined,\n symlinkTarget: string | undefined,\n): FileInfo {\n const tokenEstimate = stats.isFile() ? Math.ceil(stats.size / 4) : undefined;\n return {\n name,\n path: requestedPath,\n type: isSymlink ? 'symlink' : resolveEntryType(stats),\n size: stats.size,\n ...(tokenEstimate !== undefined ? { tokenEstimate } : {}),\n created: stats.birthtime.toISOString(),\n modified: stats.mtime.toISOString(),\n accessed: stats.atime.toISOString(),\n permissions: getPermissions(stats.mode),\n isHidden: isHidden(name),\n ...(mimeType !== undefined ? { mimeType } : {}),\n ...(symlinkTarget !== undefined ? { symlinkTarget } : {}),\n };\n}\n\nasync function getSymlinkTarget(\n pathToRead: string,\n fs: GuardedFileSystem,\n signal?: AbortSignal,\n log?: ToolCtx['log'],\n): Promise<string | undefined> {\n signal?.throwIfAborted();\n try {\n const { linkString } = await fs.readlink(pathToRead);\n return linkString;\n } catch (error) {\n rethrowIfAborted(error);\n log?.('warning', `stat: readlink failed for \"${pathToRead}\": ${String(error)}`, 'stat');\n return undefined;\n }\n}\n\nasync function getFileInfo(\n filePath: string,\n { signal, fs, log }: Pick<ToolCtx, 'fs' | 'signal' | 'log'>,\n): Promise<FileInfo> {\n signal.throwIfAborted();\n\n const {\n requestedPath,\n isSymlink,\n stats: followedStats,\n } = await fs.statDetailed(filePath, { signal });\n\n const { base: name, ext: rawExt } = parse(requestedPath);\n const mimeType = rawExt.length > 0 ? detectMimeType(requestedPath).mimeType : undefined;\n\n // For a symlink, fsStat follows the link and reports the target's metadata.\n // Report the link's own metadata instead so size/permissions/timestamps\n // describe the link the user asked about, not its target.\n let stats = followedStats;\n if (isSymlink) {\n try {\n ({ stats } = await fs.lstat(requestedPath, { signal }));\n } catch (error) {\n // A cancelled request is not a \"link metadata unavailable\" fallback.\n rethrowIfAborted(error);\n // Guarded lstat can be refused where raw lstat succeeded (e.g. an allowed\n // root that is itself a symlink, whose parent sits outside the roots).\n // Falling back to the followed stats degrades the report to the target's\n // metadata, so leave a trace instead of degrading silently.\n log?.('warning', `stat: lstat failed for \"${requestedPath}\": ${String(error)}`, 'stat');\n stats = followedStats;\n }\n }\n\n // isSymlink above is true whenever ANY ancestor is a symlink, because it\n // compares the requested path with its fully-resolved real path. Only the\n // entry's own lstat says whether THIS path is a link, so a regular file under\n // a symlinked parent is reported as a file and no readlink is attempted.\n const isOwnSymlink = stats.isSymbolicLink();\n const symlinkTarget = isOwnSymlink\n ? await getSymlinkTarget(requestedPath, fs, signal, log)\n : undefined;\n\n return buildFileInfoResult(name, requestedPath, isOwnSymlink, stats, mimeType, symlinkTarget);\n}\n\nfunction classifyTypeCounts(results: readonly PerPathResult<FileInfo>[]): {\n fileCount: number;\n dirCount: number;\n} {\n let fileCount = 0;\n let dirCount = 0;\n for (const result of results) {\n if ('error' in result) continue;\n if (result.value.type === 'directory') dirCount += 1;\n else fileCount += 1;\n }\n return { fileCount, dirCount };\n}\n\nexport const GET_FILE_INFO = defineTool({\n name: 'stat',\n title: 'Get File Info',\n description:\n 'Get metadata for one or more files or directories: size, type, permissions, MIME type, timestamps, and tokenEstimate. ' +\n 'Use tokenEstimate to pre-screen read cost before calling read. ' +\n 'Single path: pass path. Batch mode: pass paths[].',\n input: StatInputSchema,\n output: StatOutputSchema,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.NOT_FOUND,\n progress: (args) => {\n if (args.paths !== undefined) {\n return { label: 'Stat', subject: `${String(args.paths.length)} paths` };\n }\n return { label: 'Stat', subject: args.path ?? '' };\n },\n accessPaths: singleOrBatchAccessPaths,\n run: async (args, ctx) => {\n const batchInput = args.path !== undefined ? { path: args.path } : { paths: args.paths ?? [] };\n\n const batch = await runOverPaths<undefined, FileInfo>(\n batchInput,\n ctx,\n async ({ path }) => getFileInfo(path, ctx),\n { defaultErrorCode: ErrorCode.NOT_FOUND },\n );\n\n const { fileCount, dirCount } = classifyTypeCounts(batch.results);\n const perPathPayload = batch.results;\n\n let resourceUri: string | undefined;\n const resources: ContentBlock[] = [];\n if (ctx.resourceStore && batch.summary.total > 1) {\n const { entry, link } = putJsonResource(\n ctx.resourceStore,\n `${String(batch.summary.total)} paths`,\n perPathPayload,\n );\n resourceUri = entry.uri;\n resources.push(link);\n }\n\n // No `text` on purpose. The one-liner this used to build (`AGENTS.md: file,\n // 751 B`) dropped tokenEstimate, modified, mimeType and isHidden — the very\n // fields a caller asks `stat` for. Supplying no text makes this a data tool,\n // so `define.ts` renders the JSON and keeps it in `structuredContent`.\n return {\n structured: {\n results: perPathPayload,\n summary: batch.summary,\n fileCount,\n dirCount,\n ...(resourceUri ? { resourceUri } : {}),\n },\n isError: isTotalFailure(batch.summary),\n ...(resources.length > 0 ? { resources } : {}),\n };\n },\n});\n"]}
1
+ {"version":3,"file":"stat.js","sourceRoot":"","sources":["../../src/tools/stat.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { parse } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { ErrorCode, rethrowIfAborted } from '../core/errors.js';\nimport type { GuardedFileSystem, Stats } from '../core/fs.js';\nimport { isHidden } from '../core/fs.js';\nimport { detectMimeType } from '../core/mime.js';\nimport { resolveEntryType } from '../core/primitives.js';\nimport {\n FileInfoSchema,\n NonNegInt,\n OperationSummarySchema,\n PerFileErrorSchema,\n singleOrBatchAccessPaths,\n singleOrBatchPathsInput,\n} from '../core/schema.js';\nimport { putJsonResource } from '../core/store.js';\nimport { DEFAULT_SEARCH_TIMEOUT_MS } from '../core/util.js';\nimport type { PerPathResult } from './batch.js';\nimport { isTotalFailure, runOverPaths } from './batch.js';\nimport type { ToolCtx } from './define.js';\nimport { defineTool } from './define.js';\n\ntype FileInfo = z.infer<typeof FileInfoSchema>;\n\nconst StatInputSchema = singleOrBatchPathsInput({});\n\nconst StatPerPathSchema = z.strictObject({\n path: z.string().describe('Requested path'),\n value: FileInfoSchema.optional().describe('File metadata; present on success'),\n error: PerFileErrorSchema.optional().describe('Error details; present on failure'),\n});\n\nconst StatOutputSchema = z.strictObject({\n results: z\n .array(StatPerPathSchema)\n .describe('Per-path metadata results ordered to match the input paths'),\n summary: OperationSummarySchema,\n fileCount: NonNegInt.optional().describe('Number of regular files in the results'),\n dirCount: NonNegInt.optional().describe('Number of directories in the results'),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'URI to aggregated stats.json in the resource store (present when resource store is available)',\n ),\n});\n\nfunction getPermissions(mode: number): string {\n let out = '';\n for (const shift of [6, 3, 0]) {\n const bits = (mode >> shift) & 0b111;\n out += (bits & 0b100 ? 'r' : '-') + (bits & 0b010 ? 'w' : '-') + (bits & 0b001 ? 'x' : '-');\n }\n return out;\n}\n\nfunction buildFileInfoResult(\n name: string,\n requestedPath: string,\n isSymlink: boolean,\n stats: Stats,\n mimeType: string | undefined,\n symlinkTarget: string | undefined,\n): FileInfo {\n const tokenEstimate = stats.isFile() ? Math.ceil(stats.size / 4) : undefined;\n return {\n name,\n path: requestedPath,\n type: isSymlink ? 'symlink' : resolveEntryType(stats),\n size: stats.size,\n ...(tokenEstimate !== undefined ? { tokenEstimate } : {}),\n created: stats.birthtime.toISOString(),\n modified: stats.mtime.toISOString(),\n accessed: stats.atime.toISOString(),\n permissions: getPermissions(stats.mode),\n isHidden: isHidden(name),\n ...(mimeType !== undefined ? { mimeType } : {}),\n ...(symlinkTarget !== undefined ? { symlinkTarget } : {}),\n };\n}\n\nasync function getSymlinkTarget(\n pathToRead: string,\n fs: GuardedFileSystem,\n signal?: AbortSignal,\n log?: ToolCtx['log'],\n): Promise<string | undefined> {\n signal?.throwIfAborted();\n try {\n const { linkString } = await fs.readlink(pathToRead);\n return linkString;\n } catch (error) {\n rethrowIfAborted(error);\n log?.('warning', `stat: readlink failed for \"${pathToRead}\": ${String(error)}`, 'stat');\n return undefined;\n }\n}\n\nasync function getFileInfo(\n filePath: string,\n { signal, fs, log }: Pick<ToolCtx, 'fs' | 'signal' | 'log'>,\n): Promise<FileInfo> {\n signal.throwIfAborted();\n\n const {\n requestedPath,\n isSymlink,\n stats: followedStats,\n } = await fs.statDetailed(filePath, { signal });\n\n const { base: name, ext: rawExt } = parse(requestedPath);\n const mimeType = rawExt.length > 0 ? detectMimeType(requestedPath).mimeType : undefined;\n\n // For a symlink, fsStat follows the link and reports the target's metadata.\n // Report the link's own metadata instead so size/permissions/timestamps\n // describe the link the user asked about, not its target.\n let stats = followedStats;\n if (isSymlink) {\n try {\n ({ stats } = await fs.lstat(requestedPath, { signal }));\n } catch (error) {\n // A cancelled request is not a \"link metadata unavailable\" fallback.\n rethrowIfAborted(error);\n // Guarded lstat can be refused where raw lstat succeeded (e.g. an allowed\n // root that is itself a symlink, whose parent sits outside the roots).\n // Falling back to the followed stats degrades the report to the target's\n // metadata, so leave a trace instead of degrading silently.\n log?.('warning', `stat: lstat failed for \"${requestedPath}\": ${String(error)}`, 'stat');\n stats = followedStats;\n }\n }\n\n // isSymlink above is true whenever ANY ancestor is a symlink, because it\n // compares the requested path with its fully-resolved real path. Only the\n // entry's own lstat says whether THIS path is a link, so a regular file under\n // a symlinked parent is reported as a file and no readlink is attempted.\n const isOwnSymlink = stats.isSymbolicLink();\n const symlinkTarget = isOwnSymlink\n ? await getSymlinkTarget(requestedPath, fs, signal, log)\n : undefined;\n\n return buildFileInfoResult(name, requestedPath, isOwnSymlink, stats, mimeType, symlinkTarget);\n}\n\nfunction classifyTypeCounts(results: readonly PerPathResult<FileInfo>[]): {\n fileCount: number;\n dirCount: number;\n} {\n let fileCount = 0;\n let dirCount = 0;\n for (const result of results) {\n if ('error' in result) continue;\n if (result.value.type === 'directory') dirCount += 1;\n else fileCount += 1;\n }\n return { fileCount, dirCount };\n}\n\nexport const STAT = defineTool({\n name: 'stat',\n title: 'Get File Info',\n description:\n 'Get metadata for one or more files or directories: size, type, permissions, MIME type, timestamps, and tokenEstimate. ' +\n 'Use tokenEstimate to pre-screen read cost before calling read. ' +\n 'Single path: pass path. Batch mode: pass paths[].',\n input: StatInputSchema,\n output: StatOutputSchema,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.NOT_FOUND,\n progress: (args) => {\n if (args.paths !== undefined) {\n return { label: 'Stat', subject: `${String(args.paths.length)} paths` };\n }\n return { label: 'Stat', subject: args.path ?? '' };\n },\n accessPaths: singleOrBatchAccessPaths,\n run: async (args, ctx) => {\n const batchInput = args.path !== undefined ? { path: args.path } : { paths: args.paths ?? [] };\n\n const batch = await runOverPaths<undefined, FileInfo>(\n batchInput,\n ctx,\n async ({ path }) => getFileInfo(path, ctx),\n { defaultErrorCode: ErrorCode.NOT_FOUND },\n );\n\n const { fileCount, dirCount } = classifyTypeCounts(batch.results);\n const perPathPayload = batch.results;\n\n let resourceUri: string | undefined;\n const resources: ContentBlock[] = [];\n if (ctx.resourceStore && batch.summary.total > 1) {\n const { entry, link } = putJsonResource(\n ctx.resourceStore,\n `${String(batch.summary.total)} paths`,\n perPathPayload,\n );\n resourceUri = entry.uri;\n resources.push(link);\n }\n\n // No `text` on purpose. The one-liner this used to build (`AGENTS.md: file,\n // 751 B`) dropped tokenEstimate, modified, mimeType and isHidden — the very\n // fields a caller asks `stat` for. Supplying no text makes this a data tool,\n // so `define.ts` renders the JSON and keeps it in `structuredContent`.\n return {\n structured: {\n results: perPathPayload,\n summary: batch.summary,\n fileCount,\n dirCount,\n ...(resourceUri ? { resourceUri } : {}),\n },\n isError: isTotalFailure(batch.summary),\n ...(resources.length > 0 ? { resources } : {}),\n };\n },\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AASxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAuBrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkNjD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,EACtB,MAAM,GAAE,aAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CAoHjB"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AASxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAuBrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkNjD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,EACtB,MAAM,GAAE,aAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CAwHjB"}
@@ -207,7 +207,11 @@ export async function startHttpServer(port, options, config = {}) {
207
207
  };
208
208
  return c.mcp;
209
209
  }, {
210
- legacy: 'reject',
210
+ // The SDK's default: a 2025-era request is answered by a fresh instance
211
+ // from the same factory, no session. What that leg cannot do — answer an
212
+ // input_required confirmation, deliver a subscription — is refused with a
213
+ // named workaround (define.ts, resources.ts). See docs/adr/003.
214
+ legacy: 'stateless',
211
215
  onerror: (error) => {
212
216
  Logger.error('[HTTP] modern leg error:', formatUnknownErrorMessage(error));
213
217
  },
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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","sourcesContent":["// HTTP hosting: express app assembly, security middleware wiring, the modern\n// per-request server factory, and listen-filter watcher gating on POST /mcp.\nimport { createMcpExpressApp } from '@modelcontextprotocol/express';\nimport { toNodeHandler } from '@modelcontextprotocol/node';\nimport type { McpHttpHandler, ServerNotifier } from '@modelcontextprotocol/server';\nimport {\n createMcpHandler,\n DEFAULT_REQUEST_TIMEOUT_MSEC,\n isJsonContentType,\n ProtocolErrorCode,\n} from '@modelcontextprotocol/server';\n\nimport type { Server } from 'node:http';\nimport { createServer as createHttpServer } from 'node:http';\n\nimport type { Express, NextFunction, Request, Response } from 'express';\n\nimport { formatUnknownErrorMessage } from '../core/errors.js';\nimport { Logger } from '../core/observability.js';\nimport { PageSnapshotStore } from '../core/page-store.js';\nimport { PathGuard } from '../core/path.js';\nimport type { ServerOptions } from '../core/path.js';\nimport { parseTrueEnvFlag } from '../core/primitives.js';\nimport { ResourceStore } from '../core/store.js';\nimport { MIB, parseEnvInt } from '../core/util.js';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from '../core/watcher-registry.js';\nimport { createServer } from '../server.js';\nimport {\n assertHttpBindingPolicy,\n assertHttpHostPolicy,\n bearerAuthMiddleware,\n computeAllowedOriginHostnames,\n corsMiddleware,\n createRateLimiter,\n JSONRPC_SERVER_ERROR,\n protectedResourceUrl,\n resolveAllowedHosts,\n resolveTrustProxySetting,\n sendJsonRpcError,\n} from './http-policy.js';\nimport type { RuntimeConfig } from './shared.js';\nimport {\n isStructurallyValidListen,\n jsonRpcRequestId,\n listenSubscriptionUris,\n prepareListenWatchers,\n} from './shared.js';\n\nconst MAX_REQUEST_BODY_BYTES = parseEnvInt('FS_MAX_REQUEST_BYTES', 4 * MIB, 1024, 256 * MIB);\n\n/** Body-parser rejections and anything else that reaches the end of the chain. */\nfunction errorHandlerMiddleware(\n err: Error & { status?: number },\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) {\n next(err);\n return;\n }\n if (err.status === 413) {\n sendJsonRpcError(res, 413, ProtocolErrorCode.InvalidRequest, 'Request body too large');\n return;\n }\n if (err.status === 400) {\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n Logger.error('[HTTP] Unhandled middleware error:', formatUnknownErrorMessage(err));\n sendJsonRpcError(res, 500, ProtocolErrorCode.InternalError, 'Internal Server Error');\n}\n\nfunction setupExpressApp(\n httpHost: string,\n apiKey: string | undefined,\n allowedHosts: string[],\n modernNodeHandler: (req: Request, res: Response, parsedBody?: unknown) => Promise<void>,\n sharedPathGuard: PathGuard,\n sharedRegistry: WatcherRegistry,\n notifier: ServerNotifier,\n): Express {\n const allowedOriginHostnames = computeAllowedOriginHostnames(process.env['FS_ALLOWED_ORIGINS']);\n // Read once here and passed down, like every other env-derived policy input:\n // http-policy holds no state and reads no env of its own.\n const publicUrl = process.env['FS_PUBLIC_URL'];\n\n const app = createMcpExpressApp({\n host: httpHost,\n jsonLimit: `${MAX_REQUEST_BODY_BYTES}b`,\n ...(allowedHosts.length > 0 ? { allowedHosts: [...allowedHosts] } : {}),\n ...(allowedOriginHostnames.length > 0 ? { allowedOrigins: [...allowedOriginHostnames] } : {}),\n });\n\n const trustProxy = resolveTrustProxySetting(process.env['FS_TRUST_PROXY']);\n if (trustProxy !== undefined) {\n app.set('trust proxy', trustProxy);\n }\n\n if (allowedHosts.length === 0) {\n Logger.warn(\n '[HTTP] FS_ALLOW_UNRESTRICTED_HOSTS is set: binding globally without Host validation.',\n );\n }\n\n app.use('/mcp', corsMiddleware(allowedOriginHostnames));\n\n // Unconditional: the spec's rate-limit MUST is not scoped to authenticated\n // binds. A keyless bind is loopback-only, so the cap is looser, not absent.\n const rpm = parseEnvInt('FS_RATE_LIMIT_RPM', apiKey ? 120 : 6_000, 1, 100_000);\n app.use('/mcp', createRateLimiter(rpm));\n\n if (apiKey && allowedHosts.length > 0) {\n const metadataHandler = (req: Request, res: Response): void => {\n const resource = protectedResourceUrl(req, true, publicUrl);\n if (!resource) {\n // Unreachable for the unrestricted case (the gate above excludes it),\n // but defend a FS_PUBLIC_URL parse failure with a bodyless\n // 404 — a generic OAuth client cannot parse a JSON-RPC error envelope.\n res.status(404).end();\n return;\n }\n res.header('Access-Control-Allow-Origin', '*');\n res.status(200).json({\n resource: resource.href,\n bearer_methods_supported: ['header'],\n resource_name: 'filesystem-mcp',\n });\n };\n app.get(\n ['/.well-known/oauth-protected-resource', '/.well-known/oauth-protected-resource/mcp'],\n metadataHandler,\n );\n }\n\n app.get('/healthz', (_req: Request, res: Response) => {\n res.status(200).json({\n status: 'ok',\n uptime: process.uptime(),\n });\n });\n\n app.use('/mcp', bearerAuthMiddleware(apiKey, allowedHosts.length > 0, publicUrl));\n\n const resourceUpdateSink = (uri: string): void => {\n notifier.resourceUpdated(uri);\n };\n\n app.post('/mcp', (req: Request, res: Response, next: NextFunction) => {\n void (async () => {\n if (!isJsonContentType(req.headers['content-type'])) {\n sendJsonRpcError(\n res,\n 415,\n JSONRPC_SERVER_ERROR,\n 'Unsupported Media Type: Content-Type must be application/json',\n );\n return;\n }\n const parsedBody = req.body as unknown;\n if (parsedBody === undefined) {\n // Undefined tells the Node adapter to read the raw stream without our parser's limit.\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n // Only a structurally valid listen gets watchers, mirroring the stdio\n // gate. A malformed one cannot succeed downstream, so attaching handles\n // for it only gives the response-close release something to undo — and\n // everything that is not a listen takes no watchers either way.\n if (!isStructurallyValidListen(parsedBody)) {\n await modernNodeHandler(req, res, parsedBody);\n return;\n }\n\n // Reject an over-cap listen before the ack so the client does not believe\n // every requested URI is watched when the watcher budget is exhausted.\n // The per-URI `capped` failure below would also reject, but only after\n // creating and tearing down every watcher up to the cap.\n const requestedUris = listenSubscriptionUris(parsedBody);\n // Only URIs without a live watcher consume a slot; a re-listen to an\n // already-watched URI just retains another lease.\n const newUris = requestedUris.filter((uri) => !sharedRegistry.hasWatcher(uri));\n const available = MAX_WATCHERS - sharedRegistry.size();\n if (newUris.length > available) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n `subscriptions/listen names ${newUris.length} not-yet-watched URIs but only ${available} watcher slots remain (cap ${MAX_WATCHERS}). Reduce the resourceSubscriptions list.`,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n const prepared = await prepareListenWatchers(\n parsedBody,\n sharedPathGuard,\n sharedRegistry,\n resourceUpdateSink,\n );\n if (!prepared.ok) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n prepared.message,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n let released = false;\n const release = (): void => {\n if (released) return;\n released = true;\n for (const uri of prepared.acquiredUris) sharedRegistry.release(uri);\n };\n if (prepared.acquiredUris.length > 0) {\n // The attach loop awaits per-URI path validation, so the client can be\n // gone by the time it returns. `close` fires once: a listener attached\n // after it would never run and every lease this batch took would leak\n // until process shutdown.\n if (res.closed || res.destroyed || res.writableEnded) release();\n else res.once('close', release);\n }\n\n try {\n await modernNodeHandler(req, res, parsedBody);\n } catch (error) {\n release();\n throw error;\n }\n })().catch(next);\n });\n\n // Answer non-POST here rather than handing it to `modernNodeHandler`. The\n // SDK's own 405 is byte-identical to this one, but it omits the `Allow`\n // header RFC 9110 §15.5.6 requires and routes every routine GET probe through\n // the handler's `onerror` — which this server logs at error level.\n app.all('/mcp', (_req: Request, res: Response) => {\n sendJsonRpcError(res, 405, JSONRPC_SERVER_ERROR, 'Method not allowed.', null, {\n Allow: 'POST, OPTIONS',\n });\n });\n\n app.use(errorHandlerMiddleware);\n\n return app;\n}\n\nexport async function startHttpServer(\n port: number,\n options: ServerOptions,\n config: RuntimeConfig = {},\n): Promise<Server> {\n const httpHost = config.httpHost ?? '127.0.0.1';\n const { apiKey } = config;\n assertHttpBindingPolicy(httpHost, apiKey);\n const allowedHosts = resolveAllowedHosts(httpHost, process.env['FS_ALLOWED_HOSTS']);\n assertHttpHostPolicy(\n httpHost,\n allowedHosts,\n parseTrueEnvFlag(process.env['FS_ALLOW_UNRESTRICTED_HOSTS'], 'FS_ALLOW_UNRESTRICTED_HOSTS'),\n );\n\n const sharedRegistry = createWatcherRegistry();\n // One store for the whole endpoint, same lifetime and same one-credential\n // trust argument as the shared guard below: a result a tool externalized in\n // one POST must survive to the follow-up resources/read, and the modern leg\n // builds a fresh McpServer per request so a per-request store would discard\n // it immediately.\n // Fires only from a tool call, which is long after `modernHandler` below is\n // constructed, so reading it from the closure is safe.\n const sharedStore = new ResourceStore(() => {\n modernHandler.notify.resourcesChanged();\n });\n const sharedPageStore = new PageSnapshotStore();\n // One guard for the whole endpoint. The modern leg builds a fresh McpServer\n // per request, so a per-instance guard would discard every accepted access\n // grant the moment the request ended — re-prompting on each subsequent call\n // and leaving the listen-watcher path validating against a stale allowed set.\n // Grant scope is therefore the endpoint, not the connection: with API_KEY set\n // every caller presents the same key (one auth context by construction), and\n // without it the bind is loopback-only. Split into per-auth-context guards if\n // this ever serves more than one credential.\n const sharedPathGuard = new PathGuard(options);\n await sharedPathGuard.recomputeAllowedDirectories();\n\n const modernHandler: McpHttpHandler = createMcpHandler(\n async ({ era }) => {\n const c = await createServer(options, {\n watcherRegistry: sharedRegistry,\n notifier: modernHandler.notify,\n pathGuard: sharedPathGuard,\n resourceStore: sharedStore,\n pageStore: sharedPageStore,\n era,\n ...(apiKey !== undefined ? { apiKey } : {}),\n });\n const previousOnClose = c.mcp.server.onclose;\n c.mcp.server.onclose = () => {\n previousOnClose?.();\n c.disposeRuntimeState();\n };\n return c.mcp;\n },\n {\n legacy: 'reject',\n onerror: (error: Error) => {\n Logger.error('[HTTP] modern leg error:', formatUnknownErrorMessage(error));\n },\n },\n );\n const modernNodeHandler = toNodeHandler(modernHandler);\n\n const app = setupExpressApp(\n httpHost,\n apiKey,\n allowedHosts,\n modernNodeHandler,\n sharedPathGuard,\n sharedRegistry,\n modernHandler.notify,\n );\n\n const httpServer = createHttpServer(app);\n // Must exceed the idle timeout of any proxy in front of this server, or the\n // proxy reuses connections the server already closed (intermittent 502s).\n const keepAliveMs = parseEnvInt('FS_KEEPALIVE_TIMEOUT_MS', 5_000, 1_000, 600_000);\n httpServer.keepAliveTimeout = keepAliveMs;\n httpServer.headersTimeout = keepAliveMs + 5_000;\n httpServer.requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC;\n\n const onHttpServerError = (error: Error): void => {\n Logger.error('[HTTP] runtime server error', {\n host: httpHost,\n port,\n error: formatUnknownErrorMessage(error),\n });\n };\n httpServer.on('error', onHttpServerError);\n\n const teardown = (): Promise<void> => {\n sharedRegistry.destroy();\n sharedPageStore.clear();\n return modernHandler.close().catch((err: unknown) => {\n Logger.error('[HTTP] Error closing handler:', formatUnknownErrorMessage(err));\n });\n };\n const originalClose = httpServer.close.bind(httpServer);\n httpServer.close = function (callback?: (error?: Error) => void) {\n void teardown().then(() => {\n originalClose(callback);\n });\n return httpServer;\n };\n\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n void teardown();\n reject(err);\n };\n httpServer.once('error', onError);\n\n httpServer.listen(port, httpHost, () => {\n httpServer.removeListener('error', onError);\n Logger.info(`[HTTP] Server listening on http://${httpHost}:${port}`);\n resolve(httpServer);\n });\n });\n}\n"]}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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","sourcesContent":["// HTTP hosting: express app assembly, security middleware wiring, the modern\n// per-request server factory, and listen-filter watcher gating on POST /mcp.\nimport { createMcpExpressApp } from '@modelcontextprotocol/express';\nimport { toNodeHandler } from '@modelcontextprotocol/node';\nimport type { McpHttpHandler, ServerNotifier } from '@modelcontextprotocol/server';\nimport {\n createMcpHandler,\n DEFAULT_REQUEST_TIMEOUT_MSEC,\n isJsonContentType,\n ProtocolErrorCode,\n} from '@modelcontextprotocol/server';\n\nimport type { Server } from 'node:http';\nimport { createServer as createHttpServer } from 'node:http';\n\nimport type { Express, NextFunction, Request, Response } from 'express';\n\nimport { formatUnknownErrorMessage } from '../core/errors.js';\nimport { Logger } from '../core/observability.js';\nimport { PageSnapshotStore } from '../core/page-store.js';\nimport { PathGuard } from '../core/path.js';\nimport type { ServerOptions } from '../core/path.js';\nimport { parseTrueEnvFlag } from '../core/primitives.js';\nimport { ResourceStore } from '../core/store.js';\nimport { MIB, parseEnvInt } from '../core/util.js';\nimport {\n createWatcherRegistry,\n MAX_WATCHERS,\n type WatcherRegistry,\n} from '../core/watcher-registry.js';\nimport { createServer } from '../server.js';\nimport {\n assertHttpBindingPolicy,\n assertHttpHostPolicy,\n bearerAuthMiddleware,\n computeAllowedOriginHostnames,\n corsMiddleware,\n createRateLimiter,\n JSONRPC_SERVER_ERROR,\n protectedResourceUrl,\n resolveAllowedHosts,\n resolveTrustProxySetting,\n sendJsonRpcError,\n} from './http-policy.js';\nimport type { RuntimeConfig } from './shared.js';\nimport {\n isStructurallyValidListen,\n jsonRpcRequestId,\n listenSubscriptionUris,\n prepareListenWatchers,\n} from './shared.js';\n\nconst MAX_REQUEST_BODY_BYTES = parseEnvInt('FS_MAX_REQUEST_BYTES', 4 * MIB, 1024, 256 * MIB);\n\n/** Body-parser rejections and anything else that reaches the end of the chain. */\nfunction errorHandlerMiddleware(\n err: Error & { status?: number },\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) {\n next(err);\n return;\n }\n if (err.status === 413) {\n sendJsonRpcError(res, 413, ProtocolErrorCode.InvalidRequest, 'Request body too large');\n return;\n }\n if (err.status === 400) {\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n Logger.error('[HTTP] Unhandled middleware error:', formatUnknownErrorMessage(err));\n sendJsonRpcError(res, 500, ProtocolErrorCode.InternalError, 'Internal Server Error');\n}\n\nfunction setupExpressApp(\n httpHost: string,\n apiKey: string | undefined,\n allowedHosts: string[],\n modernNodeHandler: (req: Request, res: Response, parsedBody?: unknown) => Promise<void>,\n sharedPathGuard: PathGuard,\n sharedRegistry: WatcherRegistry,\n notifier: ServerNotifier,\n): Express {\n const allowedOriginHostnames = computeAllowedOriginHostnames(process.env['FS_ALLOWED_ORIGINS']);\n // Read once here and passed down, like every other env-derived policy input:\n // http-policy holds no state and reads no env of its own.\n const publicUrl = process.env['FS_PUBLIC_URL'];\n\n const app = createMcpExpressApp({\n host: httpHost,\n jsonLimit: `${MAX_REQUEST_BODY_BYTES}b`,\n ...(allowedHosts.length > 0 ? { allowedHosts: [...allowedHosts] } : {}),\n ...(allowedOriginHostnames.length > 0 ? { allowedOrigins: [...allowedOriginHostnames] } : {}),\n });\n\n const trustProxy = resolveTrustProxySetting(process.env['FS_TRUST_PROXY']);\n if (trustProxy !== undefined) {\n app.set('trust proxy', trustProxy);\n }\n\n if (allowedHosts.length === 0) {\n Logger.warn(\n '[HTTP] FS_ALLOW_UNRESTRICTED_HOSTS is set: binding globally without Host validation.',\n );\n }\n\n app.use('/mcp', corsMiddleware(allowedOriginHostnames));\n\n // Unconditional: the spec's rate-limit MUST is not scoped to authenticated\n // binds. A keyless bind is loopback-only, so the cap is looser, not absent.\n const rpm = parseEnvInt('FS_RATE_LIMIT_RPM', apiKey ? 120 : 6_000, 1, 100_000);\n app.use('/mcp', createRateLimiter(rpm));\n\n if (apiKey && allowedHosts.length > 0) {\n const metadataHandler = (req: Request, res: Response): void => {\n const resource = protectedResourceUrl(req, true, publicUrl);\n if (!resource) {\n // Unreachable for the unrestricted case (the gate above excludes it),\n // but defend a FS_PUBLIC_URL parse failure with a bodyless\n // 404 — a generic OAuth client cannot parse a JSON-RPC error envelope.\n res.status(404).end();\n return;\n }\n res.header('Access-Control-Allow-Origin', '*');\n res.status(200).json({\n resource: resource.href,\n bearer_methods_supported: ['header'],\n resource_name: 'filesystem-mcp',\n });\n };\n app.get(\n ['/.well-known/oauth-protected-resource', '/.well-known/oauth-protected-resource/mcp'],\n metadataHandler,\n );\n }\n\n app.get('/healthz', (_req: Request, res: Response) => {\n res.status(200).json({\n status: 'ok',\n uptime: process.uptime(),\n });\n });\n\n app.use('/mcp', bearerAuthMiddleware(apiKey, allowedHosts.length > 0, publicUrl));\n\n const resourceUpdateSink = (uri: string): void => {\n notifier.resourceUpdated(uri);\n };\n\n app.post('/mcp', (req: Request, res: Response, next: NextFunction) => {\n void (async () => {\n if (!isJsonContentType(req.headers['content-type'])) {\n sendJsonRpcError(\n res,\n 415,\n JSONRPC_SERVER_ERROR,\n 'Unsupported Media Type: Content-Type must be application/json',\n );\n return;\n }\n const parsedBody = req.body as unknown;\n if (parsedBody === undefined) {\n // Undefined tells the Node adapter to read the raw stream without our parser's limit.\n sendJsonRpcError(res, 400, ProtocolErrorCode.ParseError, 'Invalid JSON in request body');\n return;\n }\n // Only a structurally valid listen gets watchers, mirroring the stdio\n // gate. A malformed one cannot succeed downstream, so attaching handles\n // for it only gives the response-close release something to undo — and\n // everything that is not a listen takes no watchers either way.\n if (!isStructurallyValidListen(parsedBody)) {\n await modernNodeHandler(req, res, parsedBody);\n return;\n }\n\n // Reject an over-cap listen before the ack so the client does not believe\n // every requested URI is watched when the watcher budget is exhausted.\n // The per-URI `capped` failure below would also reject, but only after\n // creating and tearing down every watcher up to the cap.\n const requestedUris = listenSubscriptionUris(parsedBody);\n // Only URIs without a live watcher consume a slot; a re-listen to an\n // already-watched URI just retains another lease.\n const newUris = requestedUris.filter((uri) => !sharedRegistry.hasWatcher(uri));\n const available = MAX_WATCHERS - sharedRegistry.size();\n if (newUris.length > available) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n `subscriptions/listen names ${newUris.length} not-yet-watched URIs but only ${available} watcher slots remain (cap ${MAX_WATCHERS}). Reduce the resourceSubscriptions list.`,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n const prepared = await prepareListenWatchers(\n parsedBody,\n sharedPathGuard,\n sharedRegistry,\n resourceUpdateSink,\n );\n if (!prepared.ok) {\n sendJsonRpcError(\n res,\n 400,\n ProtocolErrorCode.InvalidParams,\n prepared.message,\n jsonRpcRequestId(parsedBody),\n );\n return;\n }\n\n let released = false;\n const release = (): void => {\n if (released) return;\n released = true;\n for (const uri of prepared.acquiredUris) sharedRegistry.release(uri);\n };\n if (prepared.acquiredUris.length > 0) {\n // The attach loop awaits per-URI path validation, so the client can be\n // gone by the time it returns. `close` fires once: a listener attached\n // after it would never run and every lease this batch took would leak\n // until process shutdown.\n if (res.closed || res.destroyed || res.writableEnded) release();\n else res.once('close', release);\n }\n\n try {\n await modernNodeHandler(req, res, parsedBody);\n } catch (error) {\n release();\n throw error;\n }\n })().catch(next);\n });\n\n // Answer non-POST here rather than handing it to `modernNodeHandler`. The\n // SDK's own 405 is byte-identical to this one, but it omits the `Allow`\n // header RFC 9110 §15.5.6 requires and routes every routine GET probe through\n // the handler's `onerror` — which this server logs at error level.\n app.all('/mcp', (_req: Request, res: Response) => {\n sendJsonRpcError(res, 405, JSONRPC_SERVER_ERROR, 'Method not allowed.', null, {\n Allow: 'POST, OPTIONS',\n });\n });\n\n app.use(errorHandlerMiddleware);\n\n return app;\n}\n\nexport async function startHttpServer(\n port: number,\n options: ServerOptions,\n config: RuntimeConfig = {},\n): Promise<Server> {\n const httpHost = config.httpHost ?? '127.0.0.1';\n const { apiKey } = config;\n assertHttpBindingPolicy(httpHost, apiKey);\n const allowedHosts = resolveAllowedHosts(httpHost, process.env['FS_ALLOWED_HOSTS']);\n assertHttpHostPolicy(\n httpHost,\n allowedHosts,\n parseTrueEnvFlag(process.env['FS_ALLOW_UNRESTRICTED_HOSTS'], 'FS_ALLOW_UNRESTRICTED_HOSTS'),\n );\n\n const sharedRegistry = createWatcherRegistry();\n // One store for the whole endpoint, same lifetime and same one-credential\n // trust argument as the shared guard below: a result a tool externalized in\n // one POST must survive to the follow-up resources/read, and the modern leg\n // builds a fresh McpServer per request so a per-request store would discard\n // it immediately.\n // Fires only from a tool call, which is long after `modernHandler` below is\n // constructed, so reading it from the closure is safe.\n const sharedStore = new ResourceStore(() => {\n modernHandler.notify.resourcesChanged();\n });\n const sharedPageStore = new PageSnapshotStore();\n // One guard for the whole endpoint. The modern leg builds a fresh McpServer\n // per request, so a per-instance guard would discard every accepted access\n // grant the moment the request ended — re-prompting on each subsequent call\n // and leaving the listen-watcher path validating against a stale allowed set.\n // Grant scope is therefore the endpoint, not the connection: with API_KEY set\n // every caller presents the same key (one auth context by construction), and\n // without it the bind is loopback-only. Split into per-auth-context guards if\n // this ever serves more than one credential.\n const sharedPathGuard = new PathGuard(options);\n await sharedPathGuard.recomputeAllowedDirectories();\n\n const modernHandler: McpHttpHandler = createMcpHandler(\n async ({ era }) => {\n const c = await createServer(options, {\n watcherRegistry: sharedRegistry,\n notifier: modernHandler.notify,\n pathGuard: sharedPathGuard,\n resourceStore: sharedStore,\n pageStore: sharedPageStore,\n era,\n ...(apiKey !== undefined ? { apiKey } : {}),\n });\n const previousOnClose = c.mcp.server.onclose;\n c.mcp.server.onclose = () => {\n previousOnClose?.();\n c.disposeRuntimeState();\n };\n return c.mcp;\n },\n {\n // The SDK's default: a 2025-era request is answered by a fresh instance\n // from the same factory, no session. What that leg cannot do — answer an\n // input_required confirmation, deliver a subscription — is refused with a\n // named workaround (define.ts, resources.ts). See docs/adr/003.\n legacy: 'stateless',\n onerror: (error: Error) => {\n Logger.error('[HTTP] modern leg error:', formatUnknownErrorMessage(error));\n },\n },\n );\n const modernNodeHandler = toNodeHandler(modernHandler);\n\n const app = setupExpressApp(\n httpHost,\n apiKey,\n allowedHosts,\n modernNodeHandler,\n sharedPathGuard,\n sharedRegistry,\n modernHandler.notify,\n );\n\n const httpServer = createHttpServer(app);\n // Must exceed the idle timeout of any proxy in front of this server, or the\n // proxy reuses connections the server already closed (intermittent 502s).\n const keepAliveMs = parseEnvInt('FS_KEEPALIVE_TIMEOUT_MS', 5_000, 1_000, 600_000);\n httpServer.keepAliveTimeout = keepAliveMs;\n httpServer.headersTimeout = keepAliveMs + 5_000;\n httpServer.requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC;\n\n const onHttpServerError = (error: Error): void => {\n Logger.error('[HTTP] runtime server error', {\n host: httpHost,\n port,\n error: formatUnknownErrorMessage(error),\n });\n };\n httpServer.on('error', onHttpServerError);\n\n const teardown = (): Promise<void> => {\n sharedRegistry.destroy();\n sharedPageStore.clear();\n return modernHandler.close().catch((err: unknown) => {\n Logger.error('[HTTP] Error closing handler:', formatUnknownErrorMessage(err));\n });\n };\n const originalClose = httpServer.close.bind(httpServer);\n httpServer.close = function (callback?: (error?: Error) => void) {\n void teardown().then(() => {\n originalClose(callback);\n });\n return httpServer;\n };\n\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n void teardown();\n reject(err);\n };\n httpServer.once('error', onError);\n\n httpServer.listen(port, httpHost, () => {\n httpServer.removeListener('error', onError);\n Logger.info(`[HTTP] Server listening on http://${httpHost}:${port}`);\n resolve(httpServer);\n });\n });\n}\n"]}
@@ -10,6 +10,7 @@ import type { RuntimeConfig } from './shared.js';
10
10
  * still passes `applyGrant`'s boundary and unsafe-path guards, so a client
11
11
  * cannot root-declare its way into $HOME or past FS_ROOT_BOUNDARY — a refused
12
12
  * root is skipped and its paths fail closed at validateAccess like any other.
13
+ * sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.
13
14
  */
14
15
  export declare function seedRootsFromClient(ctx: FilesystemServerContext): Promise<number>;
15
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/transport/stdio.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,oCAAoC,CAAC;AAM5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiCjD;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCvF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,GAAE,aAAkB,GAAG,iBAAiB,CAkOjG"}
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/transport/stdio.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,oCAAoC,CAAC;AAM5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiCjD;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCvF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,GAAE,aAAkB,GAAG,iBAAiB,CAkOjG"}
@@ -30,6 +30,7 @@ function cancelledRequestId(message) {
30
30
  * still passes `applyGrant`'s boundary and unsafe-path guards, so a client
31
31
  * cannot root-declare its way into $HOME or past FS_ROOT_BOUNDARY — a refused
32
32
  * root is skipped and its paths fail closed at validateAccess like any other.
33
+ * sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.
33
34
  */
34
35
  export async function seedRootsFromClient(ctx) {
35
36
  let roots;
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transport/stdio.ts"],"names":[],"mappings":"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","sourcesContent":["// stdio hosting: the pinned-instance factory, legacy roots seeding, and the\n// gated transport that attaches listen-filter watchers before the SDK sees the\n// message.\nimport type { McpServerFactory } from '@modelcontextprotocol/server';\nimport { ProtocolErrorCode } from '@modelcontextprotocol/server';\nimport {\n serveStdio,\n type StdioServerHandle,\n StdioServerTransport,\n} from '@modelcontextprotocol/server/stdio';\n\nimport { fileURLToPath } from 'node:url';\n\nimport { formatUnknownErrorMessage } from '../core/errors.js';\nimport { Logger } from '../core/observability.js';\nimport type { ServerOptions } from '../core/path.js';\nimport { PathGuard } from '../core/path.js';\nimport { createWatcherRegistry } from '../core/watcher-registry.js';\nimport type { FilesystemServerContext } from '../server.js';\nimport { createServer } from '../server.js';\nimport type { RuntimeConfig } from './shared.js';\nimport {\n isStructurallyValidListen,\n jsonRpcError,\n jsonRpcRequestId,\n listenSubscriptionUris,\n prepareListenWatchers,\n} from './shared.js';\n\ninterface StdioListenState {\n acquiredUris: string[];\n cancelled: boolean;\n delivered: boolean;\n}\n\n// Read through a function, not `state.cancelled` directly: a cancellation lands\n// between the checks, and control-flow narrowing from the first one would\n// otherwise make the second read a compile-time constant.\nfunction isListenCancelled(state: StdioListenState): boolean {\n return state.cancelled;\n}\n\nfunction cancelledRequestId(message: unknown): string | number | null {\n if (typeof message !== 'object' || message === null) return null;\n const notification = message as {\n method?: unknown;\n params?: { requestId?: unknown };\n };\n if (notification.method !== 'notifications/cancelled') return null;\n const requestId = notification.params?.requestId;\n return typeof requestId === 'string' || typeof requestId === 'number' ? requestId : null;\n}\n\n/**\n * Seed the guard's allowed roots from the client's declared workspace roots.\n * Legacy-era only: push-style `roots/list` is deprecated (SEP-2577) and throws\n * on a 2026-07-28 connection, where clients pass paths as tool arguments and\n * the access-grant round-trip covers out-of-root paths instead. Every root\n * still passes `applyGrant`'s boundary and unsafe-path guards, so a client\n * cannot root-declare its way into $HOME or past FS_ROOT_BOUNDARY — a refused\n * root is skipped and its paths fail closed at validateAccess like any other.\n */\nexport async function seedRootsFromClient(ctx: FilesystemServerContext): Promise<number> {\n let roots: readonly { uri: string }[];\n try {\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy-era-only; the modern era never reaches the hooks that call this.\n ({ roots } = await ctx.mcp.server.listRoots());\n } catch (err: unknown) {\n // Client without the roots capability (strict-capabilities throw), or the\n // request failed — either way there is nothing to seed.\n Logger.debug('[Stdio] roots/list unavailable', { error: formatUnknownErrorMessage(err) });\n return 0;\n }\n let granted = 0;\n for (const root of roots) {\n let dir: string;\n try {\n dir = fileURLToPath(root.uri);\n } catch {\n continue; // not a file:// root\n }\n try {\n if (await ctx.pathGuard.applyGrant(dir)) granted += 1;\n else Logger.debug('[Stdio] client root refused by grant policy', { dir });\n } catch (err: unknown) {\n Logger.debug('[Stdio] client root grant failed', {\n dir,\n error: formatUnknownErrorMessage(err),\n });\n }\n }\n // No list-changed notification follows a grant. Allowed roots were once listed\n // as concrete resources, and are not any more: the file template's `list()`\n // returns nothing and the other two lists never read the roots at all (see\n // resources.ts). Notifying bought the client a re-fetch of an unchanged list.\n if (granted > 0) {\n Logger.info(`[Stdio] allowed ${granted} client-declared workspace root(s)`);\n }\n return granted;\n}\n\n/**\n * Serve filesystem-mcp over stdio using modern protocol revision 2026-07-28.\n *\n * The SDK's `StdioListenRouter` acknowledges `subscriptions/listen` and routes\n * the pinned instance's outbound change notifications onto the matching\n * streams, but it exposes no listen-filter hook, so nothing attaches the\n * filesystem watcher that would produce those notifications. `serveStdio`'s\n * `transport` option is the seam: it installs its own `onmessage` synchronously\n * and only then starts the wire, so wrapping that callback afterwards gives the\n * same view of the inbound `resourceSubscriptions` filter the HTTP leg reads off\n * `req.body`. The watcher's notify sink calls `sendResourceUpdated` on the\n * pinned instance, which the router then delivers to the listening stream.\n *\n * Watcher leases are tracked by listen request ID and released on cancellation,\n * rejection, graceful completion, or connection close.\n */\nexport function startServer(options: ServerOptions, config: RuntimeConfig = {}): StdioServerHandle {\n let closed = false;\n let activeCtx: FilesystemServerContext | undefined;\n const pathGuard = new PathGuard(options);\n let pathGuardReady: Promise<void> | undefined;\n const ensurePathGuard = (): Promise<void> => {\n pathGuardReady ??= pathGuard.recomputeAllowedDirectories();\n return pathGuardReady;\n };\n // Shared with the resource contract so a `resources/subscribe` and a\n // `subscriptions/listen` naming the same URI reuse one watcher.\n const registry = createWatcherRegistry();\n const factory: McpServerFactory = async ({ era }) => {\n await ensurePathGuard();\n const c = await createServer(options, {\n watcherRegistry: registry,\n pathGuard,\n era,\n ...(config.apiKey !== undefined ? { apiKey: config.apiKey } : {}),\n });\n if (closed) {\n // The connection went away while this instance was being built.\n // `cleanupConnection` already ran and its `closed` guard makes every later\n // call a no-op, so publishing `c` would strand it: nothing would dispose\n // it. Return it unactivated — the wire is gone, so nothing serves it.\n c.disposeRuntimeState();\n return c.mcp;\n }\n activeCtx = c;\n if (era === 'legacy') {\n // Fires when the client's `notifications/initialized` lands. Safe to own:\n // the SDK's only touchpoint is its own initialized handler reading it.\n c.mcp.server.oninitialized = () => {\n void seedRootsFromClient(c);\n };\n // Re-list and grant anything new. Grants are session-additive (R8): a\n // root the client withdrew is not revoked mid-session.\n c.mcp.server.setNotificationHandler('notifications/roots/list_changed', () => {\n void seedRootsFromClient(c);\n });\n }\n return c.mcp;\n };\n\n // One sink for the whole connection. `addCallback` de-duplicates by function\n // identity, so re-using this closure makes a repeat `subscriptions/listen` on\n // an already-watched URI a no-op; a fresh closure per listen would instead\n // stack a callback every time, double-notifying and growing an\n // `activeCallbacks` set that MAX_WATCHERS does not bound (it caps watchers,\n // not callbacks). Reads `activeCtx` when it fires rather than capturing it, so\n // it never pins a disposed instance.\n const sink = (uri: string): void => {\n // A failed notify means the connection went away; nothing to recover.\n void activeCtx?.mcp.server.sendResourceUpdated({ uri }).catch((err: unknown) => {\n Logger.debug('[Stdio] resource update not delivered', {\n uri,\n error: formatUnknownErrorMessage(err),\n });\n });\n };\n\n const wire = new StdioServerTransport();\n const listens = new Map<string | number, StdioListenState>();\n\n // Deleting the entry is what makes this idempotent: a second call for the\n // same id finds nothing to release.\n const releaseListen = (id: string | number): void => {\n const state = listens.get(id);\n if (!state) return;\n listens.delete(id);\n for (const uri of state.acquiredUris) registry.release(uri);\n };\n\n const cleanupConnection = (): void => {\n if (closed) return;\n closed = true;\n for (const state of listens.values()) state.cancelled = true;\n listens.clear();\n registry.destroy();\n const ctx = activeCtx;\n activeCtx = undefined;\n // Both steps below are guarded separately, and for the same reason: this\n // also runs from `wire.onclose`, where a throw is an uncaught exception on\n // the stdin close event rather than a rejected close(). One shared catch\n // would let a failed disposal skip the unref and strand the process.\n try {\n ctx?.disposeRuntimeState();\n } catch {\n /* idempotent — disposeRuntimeState guards cleanedUp */\n }\n try {\n // The SDK's transport close pauses stdin only when no other 'data'\n // listener is left, so a fatal read error (a ReadBuffer overflow) tears\n // the connection down but leaves the handle referenced and this process\n // alive with nothing left to serve. Unref rather than pause: a listener\n // the SDK still owns keeps receiving data, the loop just stops being held\n // open for it.\n process.stdin.unref();\n } catch {\n /* a file-backed stdin is an `fs.ReadStream`, which has no `unref` */\n }\n };\n\n const send = wire.send.bind(wire);\n wire.send = async (message) => {\n if ('id' in message && ('result' in message || 'error' in message)) {\n const id = message.id;\n if (id !== undefined) releaseListen(id);\n }\n await send(message);\n };\n\n const handle = serveStdio(factory, {\n legacy: 'serve',\n transport: wire,\n onerror: (error: unknown) => {\n Logger.error('[Stdio] serve error:', formatUnknownErrorMessage(error));\n },\n });\n const onclose = wire.onclose;\n wire.onclose = () => {\n try {\n cleanupConnection();\n } finally {\n onclose?.();\n }\n };\n\n // Wrap the callback `serveStdio` just installed. Listen requests are admitted\n // in order so two requests naming the same URI cannot race the registry's\n // ref-count. Cancellation is serialized with pending admission by request ID:\n // a pending request is suppressed and releases after preparation, while an\n // active request releases before the SDK closes its stream.\n //\n // serveStdio installs its onmessage synchronously and only then starts the\n // wire; this wrapper is only correct because of that ordering. Assert it\n // rather than degrade to a silent no-op if a future SDK release changes it.\n const deliver = wire.onmessage;\n if (!deliver) {\n throw new Error(\n 'serveStdio did not install a synchronous onmessage handler; the subscriptions/listen watcher gate cannot attach. This is an SDK contract change, not a configuration error.',\n );\n }\n let gated = Promise.resolve();\n wire.onmessage = (message) => {\n const cancelId = cancelledRequestId(message);\n if (cancelId !== null) {\n const state = listens.get(cancelId);\n if (!state) {\n deliver(message);\n return;\n }\n state.cancelled = true;\n if (state.delivered) {\n releaseListen(cancelId);\n deliver(message);\n }\n return;\n }\n\n const subscriptionUris = listenSubscriptionUris(message);\n if (subscriptionUris.length === 0) {\n deliver(message);\n return;\n }\n const id = jsonRpcRequestId(message);\n if (id === null || listens.has(id)) {\n deliver(message);\n return;\n }\n const state: StdioListenState = {\n acquiredUris: [],\n cancelled: false,\n delivered: false,\n };\n listens.set(id, state);\n gated = gated\n .then(async () => {\n if (!isStructurallyValidListen(message)) {\n listens.delete(id);\n deliver(message);\n return;\n }\n if (isListenCancelled(state)) {\n listens.delete(id);\n return;\n }\n await ensurePathGuard();\n const prepared = await prepareListenWatchers(message, pathGuard, registry, sink);\n if (!prepared.ok) {\n listens.delete(id);\n await wire.send(jsonRpcError(ProtocolErrorCode.InvalidParams, prepared.message, id));\n return;\n }\n state.acquiredUris = prepared.acquiredUris;\n if (isListenCancelled(state)) {\n releaseListen(id);\n return;\n }\n state.delivered = true;\n deliver(message);\n })\n .catch(async (error: unknown) => {\n releaseListen(id);\n const failure =\n error instanceof Error ? error : new Error(formatUnknownErrorMessage(error));\n wire.onerror?.(failure);\n // The gate swallowed the message, so nothing downstream will answer it.\n // Without this the client waits out its whole request timeout.\n await wire\n .send(jsonRpcError(ProtocolErrorCode.InternalError, failure.message, id))\n .catch(() => {\n /* the wire is gone; onerror above already reported it */\n });\n });\n };\n\n return {\n close: async () => {\n try {\n cleanupConnection();\n } finally {\n await handle.close();\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transport/stdio.ts"],"names":[],"mappings":"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","sourcesContent":["// stdio hosting: the pinned-instance factory, legacy roots seeding, and the\n// gated transport that attaches listen-filter watchers before the SDK sees the\n// message.\nimport type { McpServerFactory } from '@modelcontextprotocol/server';\nimport { ProtocolErrorCode } from '@modelcontextprotocol/server';\nimport {\n serveStdio,\n type StdioServerHandle,\n StdioServerTransport,\n} from '@modelcontextprotocol/server/stdio';\n\nimport { fileURLToPath } from 'node:url';\n\nimport { formatUnknownErrorMessage } from '../core/errors.js';\nimport { Logger } from '../core/observability.js';\nimport type { ServerOptions } from '../core/path.js';\nimport { PathGuard } from '../core/path.js';\nimport { createWatcherRegistry } from '../core/watcher-registry.js';\nimport type { FilesystemServerContext } from '../server.js';\nimport { createServer } from '../server.js';\nimport type { RuntimeConfig } from './shared.js';\nimport {\n isStructurallyValidListen,\n jsonRpcError,\n jsonRpcRequestId,\n listenSubscriptionUris,\n prepareListenWatchers,\n} from './shared.js';\n\ninterface StdioListenState {\n acquiredUris: string[];\n cancelled: boolean;\n delivered: boolean;\n}\n\n// Read through a function, not `state.cancelled` directly: a cancellation lands\n// between the checks, and control-flow narrowing from the first one would\n// otherwise make the second read a compile-time constant.\nfunction isListenCancelled(state: StdioListenState): boolean {\n return state.cancelled;\n}\n\nfunction cancelledRequestId(message: unknown): string | number | null {\n if (typeof message !== 'object' || message === null) return null;\n const notification = message as {\n method?: unknown;\n params?: { requestId?: unknown };\n };\n if (notification.method !== 'notifications/cancelled') return null;\n const requestId = notification.params?.requestId;\n return typeof requestId === 'string' || typeof requestId === 'number' ? requestId : null;\n}\n\n/**\n * Seed the guard's allowed roots from the client's declared workspace roots.\n * Legacy-era only: push-style `roots/list` is deprecated (SEP-2577) and throws\n * on a 2026-07-28 connection, where clients pass paths as tool arguments and\n * the access-grant round-trip covers out-of-root paths instead. Every root\n * still passes `applyGrant`'s boundary and unsafe-path guards, so a client\n * cannot root-declare its way into $HOME or past FS_ROOT_BOUNDARY — a refused\n * root is skipped and its paths fail closed at validateAccess like any other.\n * sunset(SEP-2577): removal trigger in docs/adr/002-legacy-protocol-paths-sunset.md.\n */\nexport async function seedRootsFromClient(ctx: FilesystemServerContext): Promise<number> {\n let roots: readonly { uri: string }[];\n try {\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy-era-only; the modern era never reaches the hooks that call this.\n ({ roots } = await ctx.mcp.server.listRoots());\n } catch (err: unknown) {\n // Client without the roots capability (strict-capabilities throw), or the\n // request failed — either way there is nothing to seed.\n Logger.debug('[Stdio] roots/list unavailable', { error: formatUnknownErrorMessage(err) });\n return 0;\n }\n let granted = 0;\n for (const root of roots) {\n let dir: string;\n try {\n dir = fileURLToPath(root.uri);\n } catch {\n continue; // not a file:// root\n }\n try {\n if (await ctx.pathGuard.applyGrant(dir)) granted += 1;\n else Logger.debug('[Stdio] client root refused by grant policy', { dir });\n } catch (err: unknown) {\n Logger.debug('[Stdio] client root grant failed', {\n dir,\n error: formatUnknownErrorMessage(err),\n });\n }\n }\n // No list-changed notification follows a grant. Allowed roots were once listed\n // as concrete resources, and are not any more: the file template's `list()`\n // returns nothing and the other two lists never read the roots at all (see\n // resources.ts). Notifying bought the client a re-fetch of an unchanged list.\n if (granted > 0) {\n Logger.info(`[Stdio] allowed ${granted} client-declared workspace root(s)`);\n }\n return granted;\n}\n\n/**\n * Serve filesystem-mcp over stdio using modern protocol revision 2026-07-28.\n *\n * The SDK's `StdioListenRouter` acknowledges `subscriptions/listen` and routes\n * the pinned instance's outbound change notifications onto the matching\n * streams, but it exposes no listen-filter hook, so nothing attaches the\n * filesystem watcher that would produce those notifications. `serveStdio`'s\n * `transport` option is the seam: it installs its own `onmessage` synchronously\n * and only then starts the wire, so wrapping that callback afterwards gives the\n * same view of the inbound `resourceSubscriptions` filter the HTTP leg reads off\n * `req.body`. The watcher's notify sink calls `sendResourceUpdated` on the\n * pinned instance, which the router then delivers to the listening stream.\n *\n * Watcher leases are tracked by listen request ID and released on cancellation,\n * rejection, graceful completion, or connection close.\n */\nexport function startServer(options: ServerOptions, config: RuntimeConfig = {}): StdioServerHandle {\n let closed = false;\n let activeCtx: FilesystemServerContext | undefined;\n const pathGuard = new PathGuard(options);\n let pathGuardReady: Promise<void> | undefined;\n const ensurePathGuard = (): Promise<void> => {\n pathGuardReady ??= pathGuard.recomputeAllowedDirectories();\n return pathGuardReady;\n };\n // Shared with the resource contract so a `resources/subscribe` and a\n // `subscriptions/listen` naming the same URI reuse one watcher.\n const registry = createWatcherRegistry();\n const factory: McpServerFactory = async ({ era }) => {\n await ensurePathGuard();\n const c = await createServer(options, {\n watcherRegistry: registry,\n pathGuard,\n era,\n ...(config.apiKey !== undefined ? { apiKey: config.apiKey } : {}),\n });\n if (closed) {\n // The connection went away while this instance was being built.\n // `cleanupConnection` already ran and its `closed` guard makes every later\n // call a no-op, so publishing `c` would strand it: nothing would dispose\n // it. Return it unactivated — the wire is gone, so nothing serves it.\n c.disposeRuntimeState();\n return c.mcp;\n }\n activeCtx = c;\n if (era === 'legacy') {\n // Fires when the client's `notifications/initialized` lands. Safe to own:\n // the SDK's only touchpoint is its own initialized handler reading it.\n c.mcp.server.oninitialized = () => {\n void seedRootsFromClient(c);\n };\n // Re-list and grant anything new. Grants are session-additive (R8): a\n // root the client withdrew is not revoked mid-session.\n c.mcp.server.setNotificationHandler('notifications/roots/list_changed', () => {\n void seedRootsFromClient(c);\n });\n }\n return c.mcp;\n };\n\n // One sink for the whole connection. `addCallback` de-duplicates by function\n // identity, so re-using this closure makes a repeat `subscriptions/listen` on\n // an already-watched URI a no-op; a fresh closure per listen would instead\n // stack a callback every time, double-notifying and growing an\n // `activeCallbacks` set that MAX_WATCHERS does not bound (it caps watchers,\n // not callbacks). Reads `activeCtx` when it fires rather than capturing it, so\n // it never pins a disposed instance.\n const sink = (uri: string): void => {\n // A failed notify means the connection went away; nothing to recover.\n void activeCtx?.mcp.server.sendResourceUpdated({ uri }).catch((err: unknown) => {\n Logger.debug('[Stdio] resource update not delivered', {\n uri,\n error: formatUnknownErrorMessage(err),\n });\n });\n };\n\n const wire = new StdioServerTransport();\n const listens = new Map<string | number, StdioListenState>();\n\n // Deleting the entry is what makes this idempotent: a second call for the\n // same id finds nothing to release.\n const releaseListen = (id: string | number): void => {\n const state = listens.get(id);\n if (!state) return;\n listens.delete(id);\n for (const uri of state.acquiredUris) registry.release(uri);\n };\n\n const cleanupConnection = (): void => {\n if (closed) return;\n closed = true;\n for (const state of listens.values()) state.cancelled = true;\n listens.clear();\n registry.destroy();\n const ctx = activeCtx;\n activeCtx = undefined;\n // Both steps below are guarded separately, and for the same reason: this\n // also runs from `wire.onclose`, where a throw is an uncaught exception on\n // the stdin close event rather than a rejected close(). One shared catch\n // would let a failed disposal skip the unref and strand the process.\n try {\n ctx?.disposeRuntimeState();\n } catch {\n /* idempotent — disposeRuntimeState guards cleanedUp */\n }\n try {\n // The SDK's transport close pauses stdin only when no other 'data'\n // listener is left, so a fatal read error (a ReadBuffer overflow) tears\n // the connection down but leaves the handle referenced and this process\n // alive with nothing left to serve. Unref rather than pause: a listener\n // the SDK still owns keeps receiving data, the loop just stops being held\n // open for it.\n process.stdin.unref();\n } catch {\n /* a file-backed stdin is an `fs.ReadStream`, which has no `unref` */\n }\n };\n\n const send = wire.send.bind(wire);\n wire.send = async (message) => {\n if ('id' in message && ('result' in message || 'error' in message)) {\n const id = message.id;\n if (id !== undefined) releaseListen(id);\n }\n await send(message);\n };\n\n const handle = serveStdio(factory, {\n legacy: 'serve',\n transport: wire,\n onerror: (error: unknown) => {\n Logger.error('[Stdio] serve error:', formatUnknownErrorMessage(error));\n },\n });\n const onclose = wire.onclose;\n wire.onclose = () => {\n try {\n cleanupConnection();\n } finally {\n onclose?.();\n }\n };\n\n // Wrap the callback `serveStdio` just installed. Listen requests are admitted\n // in order so two requests naming the same URI cannot race the registry's\n // ref-count. Cancellation is serialized with pending admission by request ID:\n // a pending request is suppressed and releases after preparation, while an\n // active request releases before the SDK closes its stream.\n //\n // serveStdio installs its onmessage synchronously and only then starts the\n // wire; this wrapper is only correct because of that ordering. Assert it\n // rather than degrade to a silent no-op if a future SDK release changes it.\n const deliver = wire.onmessage;\n if (!deliver) {\n throw new Error(\n 'serveStdio did not install a synchronous onmessage handler; the subscriptions/listen watcher gate cannot attach. This is an SDK contract change, not a configuration error.',\n );\n }\n let gated = Promise.resolve();\n wire.onmessage = (message) => {\n const cancelId = cancelledRequestId(message);\n if (cancelId !== null) {\n const state = listens.get(cancelId);\n if (!state) {\n deliver(message);\n return;\n }\n state.cancelled = true;\n if (state.delivered) {\n releaseListen(cancelId);\n deliver(message);\n }\n return;\n }\n\n const subscriptionUris = listenSubscriptionUris(message);\n if (subscriptionUris.length === 0) {\n deliver(message);\n return;\n }\n const id = jsonRpcRequestId(message);\n if (id === null || listens.has(id)) {\n deliver(message);\n return;\n }\n const state: StdioListenState = {\n acquiredUris: [],\n cancelled: false,\n delivered: false,\n };\n listens.set(id, state);\n gated = gated\n .then(async () => {\n if (!isStructurallyValidListen(message)) {\n listens.delete(id);\n deliver(message);\n return;\n }\n if (isListenCancelled(state)) {\n listens.delete(id);\n return;\n }\n await ensurePathGuard();\n const prepared = await prepareListenWatchers(message, pathGuard, registry, sink);\n if (!prepared.ok) {\n listens.delete(id);\n await wire.send(jsonRpcError(ProtocolErrorCode.InvalidParams, prepared.message, id));\n return;\n }\n state.acquiredUris = prepared.acquiredUris;\n if (isListenCancelled(state)) {\n releaseListen(id);\n return;\n }\n state.delivered = true;\n deliver(message);\n })\n .catch(async (error: unknown) => {\n releaseListen(id);\n const failure =\n error instanceof Error ? error : new Error(formatUnknownErrorMessage(error));\n wire.onerror?.(failure);\n // The gate swallowed the message, so nothing downstream will answer it.\n // Without this the client waits out its whole request timeout.\n await wire\n .send(jsonRpcError(ProtocolErrorCode.InternalError, failure.message, id))\n .catch(() => {\n /* the wire is gone; onerror above already reported it */\n });\n });\n };\n\n return {\n close: async () => {\n try {\n cleanupConnection();\n } finally {\n await handle.close();\n }\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/filesystem-mcp",
3
- "version": "2.1.8",
3
+ "version": "2.3.0",
4
4
  "mcpName": "io.github.j0hanz/filesystem-mcp",
5
5
  "description": "Secure filesystem MCP server for reading, writing, searching, diffing, and patching files.",
6
6
  "author": "j0hanz",
@@ -35,9 +35,7 @@
35
35
  "check:static": "npm run build && npm run type-check && npm run type-check:test && eslint . && prettier --check . && knip",
36
36
  "fix": "npm run format && npm run lint:fix && npm run check",
37
37
  "check": "npm run check:static && npm run test",
38
- "test": "node --test --import tsx",
39
- "test:inspector": "node --test --import tsx \"__tests__/inspector-*.test.ts\"",
40
- "inspector": "npm run build && npx -y @modelcontextprotocol/inspector node dist/index.js"
38
+ "test": "node --test --import tsx"
41
39
  },
42
40
  "keywords": [
43
41
  "mcp",
@@ -66,13 +64,12 @@
66
64
  "@modelcontextprotocol/server": "2.0.0",
67
65
  "diff": "^9",
68
66
  "express": "^5.2.1",
69
- "ignore": "^7.0.6",
70
- "zod": "^4.4.3"
67
+ "ignore": "^7.0.9",
68
+ "zod": "^4.6.5"
71
69
  },
72
70
  "devDependencies": {
73
71
  "@eslint/js": "^10.0.1",
74
72
  "@modelcontextprotocol/client": "2.0.0",
75
- "@modelcontextprotocol/inspector": "^2.4.0",
76
73
  "@trivago/prettier-plugin-sort-imports": "^6.0.2",
77
74
  "@types/express": "^5.0.6",
78
75
  "@types/node": "^24",
@@ -80,9 +77,9 @@
80
77
  "eslint-config-prettier": "^10.1.8",
81
78
  "knip": "^6",
82
79
  "prettier": "^3.9.6",
83
- "tsx": "^4.23.12",
80
+ "tsx": "^4.23.13",
84
81
  "typescript": "^6",
85
- "typescript-eslint": "^8.68.0"
82
+ "typescript-eslint": "^8.70.0"
86
83
  },
87
84
  "engines": {
88
85
  "node": ">=24"
@@ -1,2 +0,0 @@
1
- export declare const DELETE_FILE: import("./define.js").DefinedTool;
2
- //# sourceMappingURL=delete-file.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete-file.d.ts","sourceRoot":"","sources":["../../src/tools/delete-file.ts"],"names":[],"mappings":"AA8YA,eAAO,MAAM,WAAW,mCAwDtB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete-file.js","sourceRoot":"","sources":["../../src/tools/delete-file.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACL,SAAS,EACT,OAAO,EACP,SAAS,EACT,WAAW,EACX,eAAe,EACf,OAAO,GACR,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IACvC,KAAK,EAAE,CAAC;SACL,KAAK,CAAC,YAAY,CAAC;SACnB,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,IAAI,CAAC;SACT,QAAQ,CAAC,qEAAqE,CAAC;IAClF,SAAS,EAAE,mBAAmB,CAC5B,4EAA4E,CAC7E;IACD,iBAAiB,EAAE,mBAAmB,CACpC,yEAAyE,CAC1E;CACF,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9C,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;CAChG,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAAC,CAAC,YAAY,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAC3C,KAAK,EAAE,wBAAwB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACzF,KAAK,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;CACnF,CAAC,CAAC;AAEH,2EAA2E;AAC3E,+EAA+E;AAC/E,+EAA+E;AAC/E,gFAAgF;AAChF,uBAAuB;AACvB,MAAM,kBAAkB,GAAG,CAAC,CAAC,YAAY,CAAC;IACxC,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,mBAAmB,CAAC;SAC1B,QAAQ,CAAC,mDAAmD,CAAC;IAChE,OAAO,EAAE,sBAAsB;CAChC,CAAC,CAAC;AAeH,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;AAE7E,SAAS,eAAe,CAAC,IAAY,EAAE,KAAc;IACnD,8EAA8E;IAC9E,2EAA2E;IAC3E,4EAA4E;IAC5E,gBAAgB;IAChB,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,OAAO,CAAC,WAAW,CACxB,IAAI,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,IAAI,CAAC,EACxD,SAAS,CAAC,SAAS,EACnB,IAAI,CACL;SACF,CAAC;IACJ,CAAC;IACD,IACE,WAAW,CAAC,KAAK,CAAC;QAClB,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,EAClF,CAAC;QACD,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC;SACzE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAC9E,CAAC;AAoBD;;;;GAIG;AACH,KAAK,UAAU,QAAQ,CACrB,SAAiB,EACjB,IAA0D,EAC1D,EAA2E;IAE3E,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAClE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,IAAI,CAAC,iBAAiB;YACtB,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,SAAS,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,EACpF,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;QACvD,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IACxE,CAAC;IAED,IAAI,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,OAAO;YACL,MAAM,EAAE,MAAM;YACd,2EAA2E;YAC3E,0EAA0E;YAC1E,sBAAsB;YACtB,OAAO,EAAE;gBACP,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,oDAAoD,EAAE;oBAChF,IAAI,EAAE,SAAS;iBAChB,CAAC;aACH;SACF,CAAC;IACJ,CAAC;IAED,IAAI,UAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACrD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;QACvD,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,QAAQ,GAAG,gBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpD,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,IAAI,CAAC,SAAS,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC/C,IAAI,CAAC;YACH,WAAW,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;QACxE,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC;IAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;AAC3F,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,SAAiB,EACjB,IAA0D,EAC1D,WAAoB,EACpB,KAA8C;IAE9C,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACnC,MAAM,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE;YACxB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,iBAAiB;SAC9B,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,gBAAgB,CAC7B,IAAgB,EAChB,IAA0D,EAC1D,GAAgC;IAEhC,IAAI,YAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACrD,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;QAC5C,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAC7D,CAAC;IAED,MAAM,eAAe,GAAG,gBAAgB,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7D,uEAAuE;IACvE,4EAA4E;IAC5E,uEAAuE;IACvE,4EAA4E;IAC5E,wDAAwD;IACxD,MAAM,eAAe,GACnB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,CAAC,KAAK,CAAC,GAAG;QACpD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,CAAC,KAAK,CAAC,GAAG;QACpD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,KAAK,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC;IACvE,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC,EAAE,CAAC;QACxF,OAAO;YACL,OAAO,EAAE;gBACP,IAAI,EAAE,IAAI,CAAC,SAAS;gBACpB,KAAK,EAAE,OAAO,CAAC,YAAY,CACzB,yCAAyC,IAAI,CAAC,QAAQ,OAAO,eAAe,uBAAuB,EACnG,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CACzB;aACF;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAC7D,CAAC;IAED,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,WAAW,CACxB,IAAgB,EAChB,IAA0D,EAC1D,GAAmD,EACnD,aAAgC;IAEhC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,WAAW,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/D,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QAC3D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,EAAE,OAAO,EAAE,IAAa,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1D,CAAC;QACD,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,OAAO;gBACL,OAAO,EAAE;oBACP,IAAI,EAAE,IAAI,CAAC,SAAS;oBACpB,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,wDAAwD,EAAE;wBACjF,IAAI,EAAE,IAAI,CAAC,SAAS;qBACrB,CAAC;iBACH;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,IAAiB,EACjB,GAAY;IAEZ,2EAA2E;IAC3E,oEAAoE;IACpE,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEvC,0CAA0C;IAC1C,MAAM,OAAO,GAAG,MAAM,iBAAiB,CACrC,KAAK,EACL,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,EAChD,oBAAoB,EACpB,GAAG,CAAC,MAAM,CACX,CAAC;IAEF,MAAM,GAAG,GAAG,IAAI,KAAK,CAAkC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrF,MAAM,KAAK,GAA0C,EAAE,CAAC;IACxD,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAClD,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;aAClF,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;;YACtF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QAC9C,GAAG,CAAC,KAAK,CAAC,GAAG;YACX,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,WAAW;YACjC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;SAC3D,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,aAAa,GAAG;QACpB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;KAC7E,CAAC,IAAI,EAAE,CAAC;IAET,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC;YACnC,EAAE,EAAE,QAAQ;YACZ,OAAO,EAAE,aAAa;YACtB,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;YAC1C,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAClB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACd,WAAW,CACT,WAAW,CAAC,EAAE,EACd,uBAAuB,CAAC,gDAAgD,EACxE;gBACE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;gBACpC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;aACjC,CACF,CACF;SACJ,CAAC,CAAC;QACH,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IAED,gEAAgE;IAChE,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CACtC,KAAK,EACL,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,aAAa,CAAC,EACzD,oBAAoB,EACpB,GAAG,CAAC,MAAM,CACX,CAAC;IAEF,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;QACjC,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YACnB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,CAAC;aAAM,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YAC1B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC/D,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;QACjC,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,GAAG,CAAC,IAAI,CAAC,GAAG;YACV,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW;YAChC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;SAC3D,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,+BAA+B;IAC/B,MAAM,OAAO,GAA0B,KAAK,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE;QAChE,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACrB,OAAO,CACL,KAAK,IAAI;YACP,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE;SACtE,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACnE,OAAO;QACL,OAAO;QACP,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE;KAC/E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;IACpC,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,aAAa;IACpB,WAAW,EACT,mHAAmH;QACnH,oDAAoD;QACpD,qGAAqG;QACrG,oFAAoF;QACpF,oEAAoE;QACpE,+CAA+C;IACjD,KAAK,EAAE,iBAAiB;IACxB,MAAM,EAAE,kBAAkB;IAC1B,WAAW,EAAE;QACX,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,KAAK;QACrB,eAAe,EAAE,IAAI;QACrB,aAAa,EAAE,KAAK;KACrB;IACD,gBAAgB,EAAE,SAAS,CAAC,OAAO;IACnC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACnB,KAAK,EAAE,QAAQ;QACf,OAAO,EACL,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC/B,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ;KAC3C,CAAC;IACF,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IACtC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACjD,qEAAqE;QACrE,4EAA4E;QAC5E,IAAI,qBAAqB,CAAC,UAAU,CAAC;YAAE,OAAO,UAAU,CAAC;QACzD,uEAAuE;QACvE,wEAAwE;QACxE,+BAA+B;QAC/B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;QAC7C,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACnD,6EAA6E;QAC7E,0EAA0E;QAC1E,mEAAmE;QACnE,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACnC,OAAO;gBACL,UAAU;gBACV,IAAI,EAAE,WAAW,IAAI,EAAE,IAAI,IAAI,EAAE,aAAa,IAAI,EAAE,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE;gBACvF,OAAO;aACR,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,CAAC,KAAK;gBAAE,OAAO,GAAG,KAAK,SAAS,CAAC;YACtC,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,UAAU,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,UAAU,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC;IAChF,CAAC;CACF,CAAC,CAAC","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\nimport { isInputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.js';\nimport {\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n isNotFoundErrno,\n Problem,\n} from '../core/errors.js';\nimport { joinRoster, pathLabel } from '../core/fmt.js';\nimport type { FileType, GuardedFileSystem } from '../core/fs.js';\nimport { choiceInput, pendingRoundTrip, readAcceptedChoice } from '../core/input-required.js';\nimport { resolveEntryType } from '../core/primitives.js';\nimport {\n defaultFalseBoolean,\n OperationSummarySchema,\n PerFileErrorSchema,\n RequiredPath,\n} from '../core/schema.js';\nimport { PARALLEL_CONCURRENCY } from '../core/util.js';\nimport { isTotalFailure } from './batch.js';\nimport type { ToolCtx } from './define.js';\nimport { defineTool } from './define.js';\n\nconst DeleteInputSchema = z.strictObject({\n paths: z\n .array(RequiredPath)\n .min(1)\n .max(1000)\n .describe('Paths to delete (max 1000); accepts files, directories, or symlinks'),\n recursive: defaultFalseBoolean(\n 'Delete directory contents recursively (required for non-empty directories)',\n ),\n ignoreIfNotExists: defaultFalseBoolean(\n 'Silently succeed if a path does not exist instead of returning an error',\n ),\n});\n\nconst DeletePerPathValueSchema = z.strictObject({\n deleted: z.boolean().describe('True when the path was removed; false when the user chose Skip'),\n});\n\nconst DeletePerPathSchema = z.strictObject({\n path: z.string().describe('Requested path'),\n value: DeletePerPathValueSchema.optional().describe('Delete outcome; present on success'),\n error: PerFileErrorSchema.optional().describe('Error details; present on failure'),\n});\n\n// One envelope for every batch tool: `read` and `stat` already answer with\n// `{ results, summary }`, so `delete` does too. The old shape switched between\n// `{ ok, path }` and `{ ok: false, failures }` depending on the outcome, which\n// left a caller parsing two schemas for one tool and never named the paths in a\n// multi-delete's text.\nconst DeleteOutputSchema = z.strictObject({\n results: z\n .array(DeletePerPathSchema)\n .describe('Per-path results ordered to match the input paths'),\n summary: OperationSummarySchema,\n});\n\ntype DeleteInput = z.infer<typeof DeleteInputSchema>;\ntype DeleteOutput = z.infer<typeof DeleteOutputSchema>;\ntype DeletePerPathResult = z.infer<typeof DeletePerPathSchema>;\n\n// Internal types for error handling\ninterface DeletedItem {\n path: string;\n}\ninterface DeleteFailure {\n path: string;\n error: Problem;\n}\n\nconst ERR_NOT_EMPTY = new Error('Directory not empty. Set recursive: true.');\n\nfunction toDeleteFailure(path: string, error: unknown): DeleteFailure {\n // A missing path reached here as the raw errno text (\"ENOENT: no such file or\n // directory, lstat 'c:\\...'\"), while `stat` reported the same condition as\n // \"Path not found\". One condition, one message — and the syscall name stays\n // off the wire.\n if (isNotFoundErrno(error)) {\n return {\n path,\n error: Problem.fromUnknown(\n new FsError(ErrorCode.NOT_FOUND, 'Path not found', path),\n ErrorCode.NOT_FOUND,\n path,\n ),\n };\n }\n if (\n isNodeError(error) &&\n (error.code === 'ENOTEMPTY' || error.code === 'EISDIR' || error.code === 'EEXIST')\n ) {\n return {\n path,\n error: Problem.fromUnknown(ERR_NOT_EMPTY, ErrorCode.INVALID_INPUT, path),\n };\n }\n return { path, error: Problem.fromUnknown(error, ErrorCode.UNKNOWN, path) };\n}\n\ntype LstatResult = Awaited<ReturnType<GuardedFileSystem['lstat']>>;\ntype ItemType = FileType;\n\n/** A path's pre-checked plan: validated, statted, and flagged if it needs confirmation. */\ninterface DeletePlan {\n inputPath: string;\n validPath: string;\n itemType: ItemType;\n firstStats: LstatResult;\n /** Recursive + non-empty directory: needs a round-trip confirmation. */\n pending: boolean;\n}\n\ntype PlanResult =\n | { status: 'fail'; failure: DeleteFailure }\n | { status: 'noop'; item: DeletedItem }\n | { status: 'plan'; plan: DeletePlan };\n\n/**\n * Phase 1 (no mutation): validate, stat, and decide whether a path needs a\n * confirmation before anything is deleted. A pending path is a recursive\n * non-empty directory; everything else is ready to delete directly.\n */\nasync function planPath(\n inputPath: string,\n args: Pick<DeleteInput, 'recursive' | 'ignoreIfNotExists'>,\n fs: Pick<GuardedFileSystem, 'pathGuard' | 'lstat' | 'hasChildrenUnchecked'>,\n): Promise<PlanResult> {\n let validPath: string;\n try {\n validPath = await fs.pathGuard.validatePathForDelete(inputPath);\n } catch (error) {\n if (\n args.ignoreIfNotExists &&\n ((isFsError(error) && error.code === ErrorCode.NOT_FOUND) || isNotFoundErrno(error))\n ) {\n return { status: 'noop', item: { path: inputPath } };\n }\n return { status: 'fail', failure: toDeleteFailure(inputPath, error) };\n }\n\n if (fs.pathGuard.isAllowedRoot(validPath)) {\n return {\n status: 'fail',\n // Keyed by the REQUESTED path, like every other failure here: handleDelete\n // orders results by input path, and a resolved key would not be found for\n // a relative request.\n failure: {\n path: inputPath,\n error: Problem.accessDenied('Deleting a workspace root directory is not allowed', {\n path: validPath,\n }),\n },\n };\n }\n\n let firstStats: LstatResult;\n try {\n firstStats = await fs.lstat(validPath);\n } catch (error) {\n if (isNotFoundErrno(error) && args.ignoreIfNotExists) {\n return { status: 'noop', item: { path: inputPath } };\n }\n return { status: 'fail', failure: toDeleteFailure(inputPath, error) };\n }\n\n const itemType = resolveEntryType(firstStats.stats);\n let hasChildren = false;\n if (args.recursive && itemType === 'directory') {\n try {\n hasChildren = await fs.hasChildrenUnchecked(validPath);\n } catch (error) {\n return { status: 'fail', failure: toDeleteFailure(inputPath, error) };\n }\n }\n const pending = hasChildren;\n return { status: 'plan', plan: { inputPath, validPath, itemType, firstStats, pending } };\n}\n\nasync function performDeletion(\n validPath: string,\n args: Pick<DeleteInput, 'recursive' | 'ignoreIfNotExists'>,\n isDirectory: boolean,\n fsOps: Pick<GuardedFileSystem, 'rm' | 'rmdir'>,\n): Promise<void> {\n if (isDirectory && !args.recursive) {\n await fsOps.rmdir(validPath);\n } else {\n await fsOps.rm(validPath, {\n recursive: args.recursive,\n force: args.ignoreIfNotExists,\n });\n }\n}\n\n/**\n * Phase 2 (mutation): TOCTOU re-stat against the plan's first stat, then\n * delete. A type or identity change across the confirmation gap fails closed\n * rather than deleting a replacement object the user never confirmed.\n */\nasync function finalizeDeletion(\n plan: DeletePlan,\n args: Pick<DeleteInput, 'recursive' | 'ignoreIfNotExists'>,\n ctx: Pick<ToolCtx, 'fs' | 'log'>,\n): Promise<{ item: DeletedItem } | { failure: DeleteFailure }> {\n let currentStats: LstatResult;\n try {\n currentStats = await ctx.fs.lstat(plan.validPath);\n } catch (error) {\n if (isNotFoundErrno(error) && args.ignoreIfNotExists) {\n return { item: { path: plan.validPath } };\n }\n return { failure: toDeleteFailure(plan.inputPath, error) };\n }\n\n const currentItemType = resolveEntryType(currentStats.stats);\n // Identity comparison, not just the coarse category: a swap during the\n // confirmation gap (delete original, create a same-named replacement) keeps\n // the type but changes dev/ino/birthtimeMs. birthtimeMs is the primary\n // cross-platform signal — dev/ino can read 0 on some non-POSIX drivers — so\n // combine all three rather than trusting dev/ino alone.\n const identityChanged =\n plan.firstStats.stats.dev !== currentStats.stats.dev ||\n plan.firstStats.stats.ino !== currentStats.stats.ino ||\n plan.firstStats.stats.birthtimeMs !== currentStats.stats.birthtimeMs;\n if (plan.itemType !== 'other' && (currentItemType !== plan.itemType || identityChanged)) {\n return {\n failure: {\n path: plan.validPath,\n error: Problem.invalidInput(\n `Delete failed: item type changed from ${plan.itemType} to ${currentItemType} during confirmation.`,\n { path: plan.validPath },\n ),\n },\n };\n }\n\n try {\n await performDeletion(plan.validPath, args, currentStats.stats.isDirectory(), ctx.fs);\n } catch (error) {\n return { failure: toDeleteFailure(plan.inputPath, error) };\n }\n\n ctx.log?.('info', `rm: ${plan.inputPath}`, 'delete');\n return { item: { path: plan.validPath } };\n}\n\n/**\n * Execute one planned path on the retry round. A pending path deletes only on\n * an accepted `confirm: true`; decline, cancel, or a missing key report\n * `CANCELLED` for that path (R3 proceed, R4/R5 cancelled). A non-pending path\n * deletes directly (R14: non-pending items proceed alongside accepted ones).\n */\nasync function executePlan(\n plan: DeletePlan,\n args: Pick<DeleteInput, 'recursive' | 'ignoreIfNotExists'>,\n ctx: Pick<ToolCtx, 'fs' | 'inputResponses' | 'log'>,\n pendingSorted: readonly string[],\n): Promise<{ item: DeletedItem } | { failure: DeleteFailure } | { skipped: true; path: string }> {\n if (plan.pending) {\n const key = `confirm_${pendingSorted.indexOf(plan.validPath)}`;\n const choice = readAcceptedChoice(ctx.inputResponses, key);\n if (choice === 'skip') {\n return { skipped: true as const, path: plan.validPath };\n }\n if (choice !== 'delete') {\n return {\n failure: {\n path: plan.validPath,\n error: Problem.cancelled('Delete cancelled: confirmation was declined or missing', {\n path: plan.validPath,\n }),\n },\n };\n }\n }\n return finalizeDeletion(plan, args, ctx);\n}\n\nasync function handleDelete(\n args: DeleteInput,\n ctx: ToolCtx,\n): Promise<DeleteOutput | InputRequiredResult> {\n // Duplicate paths collapse: results are keyed by path, so a repeated entry\n // would emit two rows for one deletion and inflate `summary.total`.\n const paths = [...new Set(args.paths)];\n\n // Phase 1 (no mutation): plan every path.\n const planned = await processInParallel(\n paths,\n (inputPath) => planPath(inputPath, args, ctx.fs),\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n const out = new Array<DeletePerPathResult | undefined>(paths.length).fill(undefined);\n const plans: { plan: DeletePlan; index: number }[] = [];\n for (const { index, value: r } of planned.results) {\n if (r.status === 'fail') out[index] = { path: r.failure.path, error: r.failure.error };\n else if (r.status === 'noop') out[index] = { path: r.item.path, value: { deleted: true } };\n else plans.push({ plan: r.plan, index });\n }\n for (const { index, error } of planned.errors) {\n out[index] = {\n path: paths[index] ?? '(unknown)',\n error: { code: ErrorCode.UNKNOWN, message: error.message },\n };\n }\n\n // Pending set: recursive + non-empty directories, sorted and de-duplicated.\n // `buildInputRequired` seals exactly this set into the requestState, and the\n // retried round recomputes it from the same args so a swapped retry (accept\n // for X, retry with Y) is rejected — the codec only proves the state was not\n // tampered, not that it matches the current request (R9).\n const pendingSorted = [\n ...new Set(plans.filter((p) => p.plan.pending).map((p) => p.plan.validPath)),\n ].sort();\n\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required (atomic — R14: nothing deleted yet, not\n // even the non-pending items in the same call); a retry whose verified\n // state does not bind this pending set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op: 'delete',\n pending: pendingSorted,\n requestState: ctx.requestState,\n clientCapabilities: ctx.clientCapabilities,\n buildInputs: (ps) =>\n ps.map((p, i) =>\n choiceInput(\n `confirm_${i}`,\n `Permanently delete \"${p}\" and all its contents? This cannot be undone.`,\n [\n { value: 'delete', title: 'Delete' },\n { value: 'skip', title: 'Skip' },\n ],\n ),\n ),\n });\n if (round !== undefined) return round;\n }\n\n // Phase 2 (mutation): execute deletions for every planned path.\n const executed = await processInParallel(\n plans,\n ({ plan }) => executePlan(plan, args, ctx, pendingSorted),\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { index, value: r } of executed.results) {\n const slot = plans[index]?.index;\n if (slot === undefined) continue;\n if ('skipped' in r) {\n out[slot] = { path: r.path, value: { deleted: false } };\n } else if ('failure' in r) {\n out[slot] = { path: r.failure.path, error: r.failure.error };\n } else if (r.item.path) {\n out[slot] = { path: r.item.path, value: { deleted: true } };\n }\n }\n for (const { index, error } of executed.errors) {\n const slot = plans[index]?.index;\n if (slot === undefined) continue;\n out[slot] = {\n path: paths[slot] ?? '(unknown)',\n error: { code: ErrorCode.UNKNOWN, message: error.message },\n };\n }\n\n // Emit in the caller's input order so `results[i]` lines up with `paths[i]`\n // the way read/stat guarantee.\n const results: DeletePerPathResult[] = paths.map((requested, i) => {\n const entry = out[i];\n return (\n entry ?? {\n path: requested,\n error: { code: ErrorCode.UNKNOWN, message: 'Unknown delete failure' },\n }\n );\n });\n\n const failed = results.filter((r) => r.error !== undefined).length;\n return {\n results,\n summary: { total: results.length, succeeded: results.length - failed, failed },\n };\n}\n\nexport const DELETE_FILE = defineTool({\n name: 'delete',\n title: 'Delete File',\n description:\n 'Permanently delete one or more files, directories, or symlinks (max 1000 per call). This action is irreversible. ' +\n 'Pass paths: [...] — there is no single-path form. ' +\n 'Non-empty directories require recursive=true and additionally prompt the user to confirm each one, ' +\n 'so the call returns without deleting anything until that confirmation comes back; ' +\n 'a client that cannot prompt gets an error naming the alternative. ' +\n 'Workspace root directories cannot be deleted.',\n input: DeleteInputSchema,\n output: DeleteOutputSchema,\n annotations: {\n readOnlyHint: false,\n idempotentHint: false,\n destructiveHint: true,\n openWorldHint: false,\n },\n defaultErrorCode: ErrorCode.UNKNOWN,\n progress: (args) => ({\n label: 'Delete',\n subject:\n args.paths.length === 1\n ? basename(args.paths[0] ?? '')\n : `${String(args.paths.length)} paths`,\n }),\n accessPaths: (args) => [...args.paths],\n run: async (args, ctx) => {\n const structured = await handleDelete(args, ctx);\n // input_required is a return value, not a completed call: surface it\n // verbatim so the executor short-circuits before building a CallToolResult.\n if (isInputRequiredResult(structured)) return structured;\n // Name every path and its outcome. The old text said \"deleted 2 paths\"\n // without saying which two, so a partial failure was unreadable without\n // parsing the structured half.\n const { failed, total } = structured.summary;\n const isError = isTotalFailure(structured.summary);\n // A lone failure carries its reason in the text. \"delete: . FAILED (0/1 ok)\"\n // made the model re-read structuredContent to learn it was ACCESS_DENIED;\n // with one path there is no ambiguity about which message applies.\n if (total === 1 && failed === 1) {\n const only = structured.results[0];\n return {\n structured,\n text: `delete: ${only?.path ?? ''} FAILED — ${only?.error?.message ?? 'unknown error'}`,\n isError,\n };\n }\n const tokens = structured.results.map((r) => {\n const label = pathLabel(r.path);\n if (r.error) return `${label} FAILED`;\n return r.value?.deleted ? label : `${label} SKIPPED`;\n });\n const ratio = failed > 0 ? ` (${String(total - failed)}/${String(total)} ok)` : '';\n return { structured, text: `delete: ${joinRoster(tokens)}${ratio}`, isError };\n },\n});\n"]}
@@ -1,2 +0,0 @@
1
- export declare const SEARCH_AND_REPLACE: import("./define.js").DefinedTool;
2
- //# sourceMappingURL=replace-in-files.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"replace-in-files.d.ts","sourceRoot":"","sources":["../../src/tools/replace-in-files.ts"],"names":[],"mappings":"AAylBA,eAAO,MAAM,kBAAkB,mCAyC7B,CAAC"}