@j0hanz/filesystem-mcp 1.19.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +333 -838
  2. package/dist/cli-help.d.ts +3 -0
  3. package/dist/cli-help.d.ts.map +1 -0
  4. package/dist/cli-help.js +169 -0
  5. package/dist/cli-help.js.map +1 -0
  6. package/dist/cli.d.ts +28 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +168 -123
  9. package/dist/cli.js.map +1 -0
  10. package/dist/core/concurrency.d.ts +70 -0
  11. package/dist/core/concurrency.d.ts.map +1 -0
  12. package/dist/core/concurrency.js +173 -0
  13. package/dist/core/concurrency.js.map +1 -0
  14. package/dist/core/config.d.ts +19 -0
  15. package/dist/core/config.d.ts.map +1 -0
  16. package/dist/core/config.js +11 -0
  17. package/dist/core/config.js.map +1 -0
  18. package/dist/core/cursor.d.ts +23 -0
  19. package/dist/core/cursor.d.ts.map +1 -0
  20. package/dist/core/cursor.js +62 -0
  21. package/dist/core/cursor.js.map +1 -0
  22. package/dist/core/errors.d.ts +96 -0
  23. package/dist/core/errors.d.ts.map +1 -0
  24. package/dist/core/errors.js +356 -0
  25. package/dist/core/errors.js.map +1 -0
  26. package/dist/core/file-uri.d.ts +24 -0
  27. package/dist/core/file-uri.d.ts.map +1 -0
  28. package/dist/core/file-uri.js +52 -0
  29. package/dist/core/file-uri.js.map +1 -0
  30. package/dist/core/fmt.d.ts +38 -0
  31. package/dist/core/fmt.d.ts.map +1 -0
  32. package/dist/core/fmt.js +109 -0
  33. package/dist/core/fmt.js.map +1 -0
  34. package/dist/core/fs.d.ts +108 -0
  35. package/dist/core/fs.d.ts.map +1 -0
  36. package/dist/core/fs.js +243 -0
  37. package/dist/core/fs.js.map +1 -0
  38. package/dist/core/glob.d.ts +41 -0
  39. package/dist/core/glob.d.ts.map +1 -0
  40. package/dist/core/glob.js +399 -0
  41. package/dist/core/glob.js.map +1 -0
  42. package/dist/core/input-required.d.ts +147 -0
  43. package/dist/core/input-required.d.ts.map +1 -0
  44. package/dist/core/input-required.js +261 -0
  45. package/dist/core/input-required.js.map +1 -0
  46. package/dist/core/mime.d.ts +15 -0
  47. package/dist/core/mime.d.ts.map +1 -0
  48. package/dist/core/mime.js +188 -0
  49. package/dist/core/mime.js.map +1 -0
  50. package/dist/core/observability.d.ts +10 -0
  51. package/dist/core/observability.d.ts.map +1 -0
  52. package/dist/core/observability.js +76 -0
  53. package/dist/core/observability.js.map +1 -0
  54. package/dist/core/page-store.d.ts +41 -0
  55. package/dist/core/page-store.d.ts.map +1 -0
  56. package/dist/core/page-store.js +74 -0
  57. package/dist/core/page-store.js.map +1 -0
  58. package/dist/core/path-completer.d.ts +10 -0
  59. package/dist/core/path-completer.d.ts.map +1 -0
  60. package/dist/core/path-completer.js +227 -0
  61. package/dist/core/path-completer.js.map +1 -0
  62. package/dist/core/path-discovery.d.ts +8 -0
  63. package/dist/core/path-discovery.d.ts.map +1 -0
  64. package/dist/core/path-discovery.js +105 -0
  65. package/dist/core/path-discovery.js.map +1 -0
  66. package/dist/core/path-utils.d.ts +9 -0
  67. package/dist/core/path-utils.d.ts.map +1 -0
  68. package/dist/core/path-utils.js +121 -0
  69. package/dist/core/path-utils.js.map +1 -0
  70. package/dist/core/path.d.ts +148 -0
  71. package/dist/core/path.d.ts.map +1 -0
  72. package/dist/core/path.js +674 -0
  73. package/dist/core/path.js.map +1 -0
  74. package/dist/core/primitives.d.ts +23 -0
  75. package/dist/core/primitives.d.ts.map +1 -0
  76. package/dist/core/primitives.js +54 -0
  77. package/dist/core/primitives.js.map +1 -0
  78. package/dist/core/read.d.ts +74 -0
  79. package/dist/core/read.d.ts.map +1 -0
  80. package/dist/core/read.js +562 -0
  81. package/dist/core/read.js.map +1 -0
  82. package/dist/core/schema.d.ts +139 -0
  83. package/dist/core/schema.d.ts.map +1 -0
  84. package/dist/core/schema.js +345 -0
  85. package/dist/core/schema.js.map +1 -0
  86. package/dist/core/search.d.ts +100 -0
  87. package/dist/core/search.d.ts.map +1 -0
  88. package/dist/core/search.js +248 -0
  89. package/dist/core/search.js.map +1 -0
  90. package/dist/core/sensitive.d.ts +6 -0
  91. package/dist/core/sensitive.d.ts.map +1 -0
  92. package/dist/core/sensitive.js +142 -0
  93. package/dist/core/sensitive.js.map +1 -0
  94. package/dist/core/store.d.ts +57 -0
  95. package/dist/core/store.d.ts.map +1 -0
  96. package/dist/core/store.js +162 -0
  97. package/dist/core/store.js.map +1 -0
  98. package/dist/core/util.d.ts +26 -0
  99. package/dist/core/util.d.ts.map +1 -0
  100. package/dist/core/util.js +66 -0
  101. package/dist/core/util.js.map +1 -0
  102. package/dist/core/watcher-registry.d.ts +68 -0
  103. package/dist/core/watcher-registry.d.ts.map +1 -0
  104. package/dist/core/watcher-registry.js +312 -0
  105. package/dist/core/watcher-registry.js.map +1 -0
  106. package/dist/http-policy.d.ts +121 -0
  107. package/dist/http-policy.d.ts.map +1 -0
  108. package/dist/http-policy.js +340 -0
  109. package/dist/http-policy.js.map +1 -0
  110. package/dist/index.d.ts +1 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +77 -41
  113. package/dist/index.js.map +1 -0
  114. package/dist/instructions.d.ts +12 -0
  115. package/dist/instructions.d.ts.map +1 -0
  116. package/dist/instructions.js +71 -0
  117. package/dist/instructions.js.map +1 -0
  118. package/dist/prompts.d.ts +8 -6
  119. package/dist/prompts.d.ts.map +1 -0
  120. package/dist/prompts.js +74 -168
  121. package/dist/prompts.js.map +1 -0
  122. package/dist/resources.d.ts +73 -9
  123. package/dist/resources.d.ts.map +1 -0
  124. package/dist/resources.js +392 -163
  125. package/dist/resources.js.map +1 -0
  126. package/dist/server.d.ts +62 -1
  127. package/dist/server.d.ts.map +1 -0
  128. package/dist/server.js +139 -1
  129. package/dist/server.js.map +1 -0
  130. package/dist/tools/batch.d.ts +89 -0
  131. package/dist/tools/batch.d.ts.map +1 -0
  132. package/dist/tools/batch.js +157 -0
  133. package/dist/tools/batch.js.map +1 -0
  134. package/dist/tools/create.d.ts +2 -0
  135. package/dist/tools/create.d.ts.map +1 -0
  136. package/dist/tools/create.js +142 -0
  137. package/dist/tools/create.js.map +1 -0
  138. package/dist/tools/define.d.ts +110 -0
  139. package/dist/tools/define.d.ts.map +1 -0
  140. package/dist/tools/define.js +394 -0
  141. package/dist/tools/define.js.map +1 -0
  142. package/dist/tools/delete-file.d.ts +2 -4
  143. package/dist/tools/delete-file.d.ts.map +1 -0
  144. package/dist/tools/delete-file.js +330 -87
  145. package/dist/tools/delete-file.js.map +1 -0
  146. package/dist/tools/diff.d.ts +2 -0
  147. package/dist/tools/diff.d.ts.map +1 -0
  148. package/dist/tools/diff.js +94 -0
  149. package/dist/tools/diff.js.map +1 -0
  150. package/dist/tools/edit.d.ts +2 -0
  151. package/dist/tools/edit.d.ts.map +1 -0
  152. package/dist/tools/edit.js +442 -0
  153. package/dist/tools/edit.js.map +1 -0
  154. package/dist/tools/index.d.ts +26 -0
  155. package/dist/tools/index.d.ts.map +1 -0
  156. package/dist/tools/index.js +52 -0
  157. package/dist/tools/index.js.map +1 -0
  158. package/dist/tools/list.d.ts +2 -0
  159. package/dist/tools/list.d.ts.map +1 -0
  160. package/dist/tools/list.js +291 -0
  161. package/dist/tools/list.js.map +1 -0
  162. package/dist/tools/move.d.ts +2 -0
  163. package/dist/tools/move.d.ts.map +1 -0
  164. package/dist/tools/move.js +321 -0
  165. package/dist/tools/move.js.map +1 -0
  166. package/dist/tools/patch.d.ts +2 -0
  167. package/dist/tools/patch.d.ts.map +1 -0
  168. package/dist/tools/patch.js +155 -0
  169. package/dist/tools/patch.js.map +1 -0
  170. package/dist/tools/progress.d.ts +52 -0
  171. package/dist/tools/progress.d.ts.map +1 -0
  172. package/dist/tools/progress.js +172 -0
  173. package/dist/tools/progress.js.map +1 -0
  174. package/dist/tools/read.d.ts +2 -4
  175. package/dist/tools/read.d.ts.map +1 -0
  176. package/dist/tools/read.js +398 -152
  177. package/dist/tools/read.js.map +1 -0
  178. package/dist/tools/replace-in-files.d.ts +2 -4
  179. package/dist/tools/replace-in-files.d.ts.map +1 -0
  180. package/dist/tools/replace-in-files.js +435 -264
  181. package/dist/tools/replace-in-files.js.map +1 -0
  182. package/dist/tools/roots.d.ts +2 -4
  183. package/dist/tools/roots.d.ts.map +1 -0
  184. package/dist/tools/roots.js +29 -52
  185. package/dist/tools/roots.js.map +1 -0
  186. package/dist/tools/search-content.d.ts +2 -4
  187. package/dist/tools/search-content.d.ts.map +1 -0
  188. package/dist/tools/search-content.js +262 -228
  189. package/dist/tools/search-content.js.map +1 -0
  190. package/dist/tools/search-files.d.ts +2 -4
  191. package/dist/tools/search-files.d.ts.map +1 -0
  192. package/dist/tools/search-files.js +162 -161
  193. package/dist/tools/search-files.js.map +1 -0
  194. package/dist/tools/stat.d.ts +2 -4
  195. package/dist/tools/stat.d.ts.map +1 -0
  196. package/dist/tools/stat.js +202 -62
  197. package/dist/tools/stat.js.map +1 -0
  198. package/dist/transport/http.d.ts +5 -0
  199. package/dist/transport/http.d.ts.map +1 -0
  200. package/dist/transport/http.js +276 -0
  201. package/dist/transport/http.js.map +1 -0
  202. package/dist/transport/shared.d.ts +45 -0
  203. package/dist/transport/shared.d.ts.map +1 -0
  204. package/dist/transport/shared.js +65 -0
  205. package/dist/transport/shared.js.map +1 -0
  206. package/dist/transport/stdio.d.ts +32 -0
  207. package/dist/transport/stdio.d.ts.map +1 -0
  208. package/dist/transport/stdio.js +279 -0
  209. package/dist/transport/stdio.js.map +1 -0
  210. package/dist/transport.d.ts +5 -0
  211. package/dist/transport.d.ts.map +1 -0
  212. package/dist/transport.js +4 -0
  213. package/dist/transport.js.map +1 -0
  214. package/package.json +37 -46
  215. package/dist/assets/logo.svg +0 -35
  216. package/dist/completions.d.ts +0 -2
  217. package/dist/completions.js +0 -572
  218. package/dist/config.d.ts +0 -119
  219. package/dist/config.js +0 -31
  220. package/dist/lib/abort.d.ts +0 -7
  221. package/dist/lib/abort.js +0 -81
  222. package/dist/lib/constants.d.ts +0 -42
  223. package/dist/lib/constants.js +0 -313
  224. package/dist/lib/errors.d.ts +0 -28
  225. package/dist/lib/errors.js +0 -301
  226. package/dist/lib/file-operations/core.d.ts +0 -53
  227. package/dist/lib/file-operations/core.js +0 -144
  228. package/dist/lib/file-operations/metadata.d.ts +0 -77
  229. package/dist/lib/file-operations/metadata.js +0 -878
  230. package/dist/lib/file-operations/search.d.ts +0 -46
  231. package/dist/lib/file-operations/search.js +0 -1194
  232. package/dist/lib/file-operations/traversal.d.ts +0 -40
  233. package/dist/lib/file-operations/traversal.js +0 -310
  234. package/dist/lib/fs-helpers.d.ts +0 -46
  235. package/dist/lib/fs-helpers.js +0 -606
  236. package/dist/lib/globs.d.ts +0 -2
  237. package/dist/lib/globs.js +0 -19
  238. package/dist/lib/logger.d.ts +0 -27
  239. package/dist/lib/logger.js +0 -91
  240. package/dist/lib/observability.d.ts +0 -33
  241. package/dist/lib/observability.js +0 -373
  242. package/dist/lib/paths.d.ts +0 -35
  243. package/dist/lib/paths.js +0 -634
  244. package/dist/lib/resource-store.d.ts +0 -28
  245. package/dist/lib/resource-store.js +0 -195
  246. package/dist/lib/utils.d.ts +0 -7
  247. package/dist/lib/utils.js +0 -37
  248. package/dist/lib/zod-codecs.d.ts +0 -2
  249. package/dist/lib/zod-codecs.js +0 -18
  250. package/dist/pkg-info.d.ts +0 -7
  251. package/dist/pkg-info.js +0 -9
  252. package/dist/resources/generated-instructions.d.ts +0 -1
  253. package/dist/resources/generated-instructions.js +0 -105
  254. package/dist/resources/tool-catalog.d.ts +0 -2
  255. package/dist/resources/tool-catalog.js +0 -78
  256. package/dist/resources/tool-info.d.ts +0 -9
  257. package/dist/resources/tool-info.js +0 -206
  258. package/dist/resources/workflows.d.ts +0 -1
  259. package/dist/resources/workflows.js +0 -72
  260. package/dist/schemas.d.ts +0 -505
  261. package/dist/schemas.js +0 -676
  262. package/dist/server/bootstrap.d.ts +0 -6
  263. package/dist/server/bootstrap.js +0 -563
  264. package/dist/server/event-store.d.ts +0 -18
  265. package/dist/server/event-store.js +0 -71
  266. package/dist/server/roots-manager.d.ts +0 -28
  267. package/dist/server/roots-manager.js +0 -227
  268. package/dist/server/task-store.d.ts +0 -10
  269. package/dist/server/task-store.js +0 -91
  270. package/dist/tools/apply-patch.d.ts +0 -4
  271. package/dist/tools/apply-patch.js +0 -235
  272. package/dist/tools/calculate-hash.d.ts +0 -4
  273. package/dist/tools/calculate-hash.js +0 -187
  274. package/dist/tools/contract.d.ts +0 -50
  275. package/dist/tools/contract.js +0 -1
  276. package/dist/tools/create-directory.d.ts +0 -4
  277. package/dist/tools/create-directory.js +0 -68
  278. package/dist/tools/diff-files.d.ts +0 -4
  279. package/dist/tools/diff-files.js +0 -136
  280. package/dist/tools/edit-file.d.ts +0 -4
  281. package/dist/tools/edit-file.js +0 -256
  282. package/dist/tools/icons.d.ts +0 -15
  283. package/dist/tools/icons.js +0 -24
  284. package/dist/tools/list-directory.d.ts +0 -4
  285. package/dist/tools/list-directory.js +0 -218
  286. package/dist/tools/move-file.d.ts +0 -4
  287. package/dist/tools/move-file.js +0 -178
  288. package/dist/tools/read-multiple.d.ts +0 -4
  289. package/dist/tools/read-multiple.js +0 -207
  290. package/dist/tools/shared.d.ts +0 -172
  291. package/dist/tools/shared.js +0 -599
  292. package/dist/tools/stat-many.d.ts +0 -4
  293. package/dist/tools/stat-many.js +0 -104
  294. package/dist/tools/task-support.d.ts +0 -29
  295. package/dist/tools/task-support.js +0 -472
  296. package/dist/tools/tree.d.ts +0 -4
  297. package/dist/tools/tree.js +0 -102
  298. package/dist/tools/write-file.d.ts +0 -4
  299. package/dist/tools/write-file.js +0 -60
  300. package/dist/tools.d.ts +0 -5
  301. package/dist/tools.js +0 -54
@@ -1,227 +0,0 @@
1
- import {} from '@modelcontextprotocol/server';
2
- import { channel } from 'node:diagnostics_channel';
3
- import { realpath } from 'node:fs/promises';
4
- import { z } from 'zod';
5
- import { assertNotAborted, createTimedAbortSignal, withAbort, } from '../lib/abort.js';
6
- import { getInitHandshakeTimeoutMs } from '../lib/constants.js';
7
- import { formatUnknownErrorMessage } from '../lib/errors.js';
8
- import { Logger, logToMcp } from '../lib/logger.js';
9
- import { getValidRootDirectories, isPathWithinDirectories, normalizePath, resolveAllowedDirectoriesState, setAllowedDirectoriesStateResolved, } from '../lib/paths.js';
10
- import { debounce, isRecord } from '../lib/utils.js';
11
- const ROOTS_TIMEOUT_MS = 5000;
12
- const ROOTS_DEBOUNCE_MS = 100;
13
- const LIFECYCLE_CHANNEL = channel('filesystem-mcp:lifecycle');
14
- function normalizeCLIDirectories(dirs) {
15
- const normalized = [];
16
- for (const dir of dirs) {
17
- const trimmed = dir.trim();
18
- if (trimmed.length === 0)
19
- continue;
20
- normalized.push(normalizePath(trimmed));
21
- }
22
- return normalized;
23
- }
24
- const RootSchema = z.strictObject({
25
- uri: z.string(),
26
- name: z.string().optional(),
27
- });
28
- const RootsResponseSchema = z.strictObject({
29
- roots: z.array(RootSchema).optional(),
30
- });
31
- function isRoot(value) {
32
- return isRecord(value) && typeof value['uri'] === 'string';
33
- }
34
- function normalizeRoot(root) {
35
- return root.name ? { uri: root.uri, name: root.name } : { uri: root.uri };
36
- }
37
- function extractRoots(value) {
38
- const parsed = RootsResponseSchema.safeParse(value);
39
- if (!parsed.success || !parsed.data.roots) {
40
- return [];
41
- }
42
- const roots = [];
43
- for (const root of parsed.data.roots) {
44
- if (isRoot(root)) {
45
- roots.push(normalizeRoot(root));
46
- }
47
- }
48
- return roots;
49
- }
50
- async function resolveRootDirectories(roots) {
51
- if (roots.length === 0)
52
- return [];
53
- const { signal, cleanup } = createTimedAbortSignal(undefined, ROOTS_TIMEOUT_MS);
54
- try {
55
- return await getValidRootDirectories(roots, signal);
56
- }
57
- finally {
58
- cleanup();
59
- }
60
- }
61
- async function isRootWithinBaseline(normalizedRoot, baseline, signal) {
62
- if (!isPathWithinDirectories(normalizedRoot, baseline)) {
63
- return false;
64
- }
65
- try {
66
- assertNotAborted(signal);
67
- const realPath = await withAbort(realpath(normalizedRoot), signal);
68
- const normalizedReal = normalizePath(realPath);
69
- return isPathWithinDirectories(normalizedReal, baseline);
70
- }
71
- catch {
72
- return false;
73
- }
74
- }
75
- async function filterRootsWithinBaseline(roots, baseline, signal) {
76
- const normalizedBaseline = normalizeCLIDirectories(baseline);
77
- const normalizedRoots = roots.map(normalizePath);
78
- if (normalizedRoots.length === 0)
79
- return [];
80
- const results = await Promise.allSettled(normalizedRoots.map((normalizedRoot) => isRootWithinBaseline(normalizedRoot, normalizedBaseline, signal)));
81
- return normalizedRoots.filter((_, i) => {
82
- const result = results[i];
83
- return result?.status === 'fulfilled' && result.value;
84
- });
85
- }
86
- export class RootsManager {
87
- _debouncedUpdate;
88
- rootDirectories = [];
89
- allowedDirectoriesState = {
90
- primary: [],
91
- expanded: [],
92
- };
93
- clientInitialized = false;
94
- initTimer;
95
- // Guard concurrent root refreshes; if one is already running we queue one
96
- // replay so the last-known state still gets applied after completion.
97
- updatingRoots = false;
98
- // Tracks whether a roots change arrived while the previous refresh ran.
99
- pendingRootsUpdate = false;
100
- options;
101
- loggingState;
102
- constructor(options, loggingState) {
103
- this.options = options;
104
- this.loggingState = loggingState;
105
- }
106
- isInitialized() {
107
- return this.clientInitialized;
108
- }
109
- destroy() {
110
- if (this.initTimer) {
111
- clearTimeout(this.initTimer);
112
- this.initTimer = undefined;
113
- }
114
- if (this._debouncedUpdate) {
115
- this._debouncedUpdate.cancel();
116
- this._debouncedUpdate = undefined;
117
- }
118
- }
119
- getAllowedDirectoriesState() {
120
- return {
121
- primary: [...this.allowedDirectoriesState.primary],
122
- expanded: [...this.allowedDirectoriesState.expanded],
123
- };
124
- }
125
- logMissingDirectoriesIfNeeded(server) {
126
- if (this.allowedDirectoriesState.expanded.length === 0) {
127
- this.logMissingDirectories(server);
128
- }
129
- }
130
- registerHandlers(server, onInitTimeout) {
131
- const initHandshakeTimeoutMs = getInitHandshakeTimeoutMs();
132
- server.server.setNotificationHandler('notifications/initialized', async () => {
133
- if (this.initTimer) {
134
- clearTimeout(this.initTimer);
135
- this.initTimer = undefined;
136
- }
137
- this.clientInitialized = true;
138
- await this.updateRootsFromClient(server);
139
- });
140
- server.server.setNotificationHandler('notifications/roots/list_changed', () => {
141
- if (!this.clientInitialized)
142
- return;
143
- this.scheduleRootsUpdate(server);
144
- });
145
- this.initTimer = setTimeout(() => {
146
- if (!this.clientInitialized) {
147
- if (LIFECYCLE_CHANNEL.hasSubscribers) {
148
- LIFECYCLE_CHANNEL.publish({
149
- phase: 'init_timeout',
150
- timeoutMs: initHandshakeTimeoutMs,
151
- });
152
- }
153
- logToMcp(server, 'warning', `Client did not send notifications/initialized within ${String(initHandshakeTimeoutMs)}ms`, this.loggingState.minimumLevel);
154
- onInitTimeout?.();
155
- }
156
- this.initTimer = undefined;
157
- }, initHandshakeTimeoutMs);
158
- this.initTimer.unref();
159
- }
160
- async recomputeAllowedDirectories() {
161
- const cliAllowedDirs = normalizeCLIDirectories(this.options.cliAllowedDirs ?? []);
162
- const allowCwd = Boolean(this.options.allowCwd);
163
- const allowCwdDirs = allowCwd ? [normalizePath(process.cwd())] : [];
164
- const baseline = [...cliAllowedDirs, ...allowCwdDirs];
165
- const { signal, cleanup } = createTimedAbortSignal(undefined, ROOTS_TIMEOUT_MS);
166
- try {
167
- const rootsToInclude = baseline.length > 0
168
- ? await filterRootsWithinBaseline(this.rootDirectories, baseline, signal)
169
- : this.rootDirectories;
170
- const combined = [...baseline, ...rootsToInclude];
171
- const nextState = await resolveAllowedDirectoriesState(combined, signal);
172
- this.allowedDirectoriesState = nextState;
173
- setAllowedDirectoriesStateResolved(nextState);
174
- }
175
- finally {
176
- cleanup();
177
- }
178
- }
179
- scheduleRootsUpdate(server) {
180
- this._debouncedUpdate ??= debounce((s) => {
181
- void this.updateRootsFromClient(s);
182
- }, ROOTS_DEBOUNCE_MS);
183
- this._debouncedUpdate(server);
184
- }
185
- logMissingDirectories(server) {
186
- if (this.options.allowCwd) {
187
- logToMcp(server, 'notice', 'No allowed directories specified. Using the current working directory as an allowed directory.', this.loggingState.minimumLevel);
188
- return;
189
- }
190
- logToMcp(server, 'warning', 'No allowed directories specified. Please provide directories as command-line arguments or enable --allow-cwd to use the current working directory.', this.loggingState.minimumLevel);
191
- }
192
- async updateRootsFromClient(server) {
193
- // Guard against concurrent executions: if one is already running, queue a
194
- // single retry so the last-known state is always applied after completion.
195
- if (this.updatingRoots) {
196
- this.pendingRootsUpdate = true;
197
- return;
198
- }
199
- this.updatingRoots = true;
200
- try {
201
- const clientCapabilities = server.server.getClientCapabilities();
202
- if (!clientCapabilities?.roots) {
203
- this.rootDirectories = [];
204
- }
205
- else {
206
- const rootsResult = await server.server.listRoots(undefined, {
207
- timeout: ROOTS_TIMEOUT_MS,
208
- });
209
- const roots = extractRoots(rootsResult);
210
- this.rootDirectories = await resolveRootDirectories(roots);
211
- }
212
- }
213
- catch (error) {
214
- logToMcp(server, 'debug', `[DEBUG] MCP Roots protocol unavailable or failed: ${formatUnknownErrorMessage(error)}`, this.loggingState.minimumLevel);
215
- }
216
- finally {
217
- await this.recomputeAllowedDirectories();
218
- Logger.info(`Roots updated: ${this.rootDirectories.length} root(s), ${this.allowedDirectoriesState.expanded.length} allowed dir(s)`);
219
- this.updatingRoots = false;
220
- // If a change arrived while we were running, apply it now.
221
- if (this.pendingRootsUpdate) {
222
- this.pendingRootsUpdate = false;
223
- void this.updateRootsFromClient(server);
224
- }
225
- }
226
- }
227
- }
@@ -1,10 +0,0 @@
1
- import { InMemoryTaskStore, type Result, type Task } from '@modelcontextprotocol/server';
2
- export declare class ResultAwareInMemoryTaskStore extends InMemoryTaskStore {
3
- private readonly cancelledResults;
4
- private evictExpired;
5
- getTaskResult(taskId: string, sessionId?: string): Promise<Result>;
6
- storeTaskResult(taskId: string, status: 'completed' | 'failed', result: Result, sessionId?: string): Promise<void>;
7
- updateTaskStatus(taskId: string, status: Task['status'], statusMessage?: string, sessionId?: string): Promise<void>;
8
- cleanup(): void;
9
- }
10
- export declare function createTaskStore(): ResultAwareInMemoryTaskStore;
@@ -1,91 +0,0 @@
1
- import { InMemoryTaskStore, } from '@modelcontextprotocol/server';
2
- import { CANCELLED_RESULT_TTL_MS } from '../lib/constants.js';
3
- import { ErrorCode } from '../lib/errors.js';
4
- const DEFAULT_CANCELLED_STATUS_MESSAGE = 'Client cancelled task execution.';
5
- function getTaskKey(taskId, sessionId) {
6
- return `${sessionId ?? ''}:${taskId}`;
7
- }
8
- function buildCancelledTaskResult(statusMessage) {
9
- return {
10
- content: [
11
- {
12
- type: 'text',
13
- text: `Error [${ErrorCode.CANCELLED}]: ${statusMessage ?? DEFAULT_CANCELLED_STATUS_MESSAGE}`,
14
- },
15
- ],
16
- isError: true,
17
- errorCode: ErrorCode.CANCELLED,
18
- };
19
- }
20
- export class ResultAwareInMemoryTaskStore extends InMemoryTaskStore {
21
- cancelledResults = new Map();
22
- evictExpired() {
23
- const now = Date.now();
24
- for (const [key, entry] of this.cancelledResults) {
25
- if (now - entry.createdAt > CANCELLED_RESULT_TTL_MS) {
26
- this.cancelledResults.delete(key);
27
- }
28
- }
29
- }
30
- async getTaskResult(taskId, sessionId) {
31
- this.evictExpired();
32
- try {
33
- return await super.getTaskResult(taskId, sessionId);
34
- }
35
- catch (error) {
36
- const task = await super.getTask(taskId, sessionId);
37
- if (task?.status !== 'cancelled') {
38
- throw error;
39
- }
40
- const key = getTaskKey(taskId, sessionId);
41
- const existing = this.cancelledResults.get(key);
42
- if (existing)
43
- return existing.result;
44
- const result = buildCancelledTaskResult(task.statusMessage);
45
- this.cancelledResults.set(key, { result, createdAt: Date.now() });
46
- return result;
47
- }
48
- }
49
- async storeTaskResult(taskId, status, result, sessionId) {
50
- try {
51
- await super.storeTaskResult(taskId, status, result, sessionId);
52
- if (status !== 'failed') {
53
- this.cancelledResults.delete(getTaskKey(taskId, sessionId));
54
- }
55
- }
56
- catch (error) {
57
- const task = await super.getTask(taskId, sessionId);
58
- if (task?.status !== 'cancelled') {
59
- throw error;
60
- }
61
- const key = getTaskKey(taskId, sessionId);
62
- const existing = this.cancelledResults.get(key);
63
- this.cancelledResults.set(key, {
64
- result: existing?.result ?? result,
65
- createdAt: existing?.createdAt ?? Date.now(),
66
- });
67
- }
68
- }
69
- async updateTaskStatus(taskId, status, statusMessage, sessionId) {
70
- await super.updateTaskStatus(taskId, status, statusMessage, sessionId);
71
- const key = getTaskKey(taskId, sessionId);
72
- if (status === 'cancelled') {
73
- const existing = this.cancelledResults.get(key);
74
- this.cancelledResults.set(key, {
75
- result: existing?.result ?? buildCancelledTaskResult(statusMessage),
76
- createdAt: existing?.createdAt ?? Date.now(),
77
- });
78
- return;
79
- }
80
- if (status === 'completed' || status === 'failed') {
81
- this.cancelledResults.delete(key);
82
- }
83
- }
84
- cleanup() {
85
- this.cancelledResults.clear();
86
- super.cleanup();
87
- }
88
- }
89
- export function createTaskStore() {
90
- return new ResultAwareInMemoryTaskStore();
91
- }
@@ -1,4 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
3
- export declare const APPLY_PATCH_TOOL: ToolContract;
4
- export declare function registerApplyPatchTool(server: McpServer, options?: ToolRegistrationOptions): void;
@@ -1,235 +0,0 @@
1
- import { stat } from 'node:fs/promises';
2
- import { basename, resolve } from 'node:path';
3
- import { applyPatch, parsePatch } from 'diff';
4
- import { withAbort } from '../lib/abort.js';
5
- import { MAX_TEXT_FILE_SIZE, PARALLEL_CONCURRENCY } from '../lib/constants.js';
6
- import { ErrorCode, McpError } from '../lib/errors.js';
7
- import { atomicWriteFile, processInParallel, readFileWithStats, } from '../lib/fs-helpers.js';
8
- import { Logger } from '../lib/logger.js';
9
- import { assertAllowedFileAccess, validateExistingPath } from '../lib/paths.js';
10
- import { ApplyPatchInputSchema, ApplyPatchOutputSchema } from '../schemas.js';
11
- import { FILE_EDIT_ICONS } from './icons.js';
12
- import { buildStructuredError, buildToolErrorResponse, buildToolResponse, DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS, executeToolWithDiagnostics, } from './shared.js';
13
- import { registerStandardTool } from './task-support.js';
14
- export const APPLY_PATCH_TOOL = {
15
- name: 'apply_patch',
16
- title: 'Apply Patch',
17
- description: 'Apply a unified diff patch to one or more files. ' +
18
- 'Single-file: throws on failure. Multi-file: best-effort per file with `results[]`. ' +
19
- 'Workflow: `diff_files` \u2192 `apply_patch(dryRun:true)` \u2192 `apply_patch`. ' +
20
- 'On failure, regenerate the patch from current file content.',
21
- inputSchema: ApplyPatchInputSchema,
22
- outputSchema: ApplyPatchOutputSchema,
23
- annotations: DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
24
- icons: FILE_EDIT_ICONS,
25
- nuances: [
26
- 'Multi-file patches use `path` as base directory; per-file results in `results[]`.',
27
- ],
28
- taskSupport: 'optional',
29
- };
30
- function assertPatchTargetSizeWithinLimit(filePath, size, maxFileSize) {
31
- if (size <= maxFileSize)
32
- return;
33
- throw new McpError(ErrorCode.TOO_LARGE, `File too large for patch (${size} bytes > ${maxFileSize} bytes).`, filePath, { size, maxFileSize });
34
- }
35
- function countStructuredPatchStats(diff) {
36
- let linesAdded = 0;
37
- let linesRemoved = 0;
38
- for (const hunk of diff.hunks) {
39
- for (const line of hunk.lines) {
40
- if (line.startsWith('+'))
41
- linesAdded++;
42
- else if (line.startsWith('-'))
43
- linesRemoved++;
44
- }
45
- }
46
- return { hunksApplied: diff.hunks.length, linesAdded, linesRemoved };
47
- }
48
- function stripGitPrefix(fileName) {
49
- return fileName.startsWith('a/') || fileName.startsWith('b/')
50
- ? fileName.slice(2)
51
- : fileName;
52
- }
53
- function extractPatchTargetPath(diff) {
54
- if (diff.newFileName)
55
- return stripGitPrefix(diff.newFileName);
56
- if (diff.oldFileName)
57
- return stripGitPrefix(diff.oldFileName);
58
- return undefined;
59
- }
60
- async function applyDiff(filePath, diff, options, signal) {
61
- const validPath = await validateExistingPath(filePath, signal);
62
- assertAllowedFileAccess(filePath, validPath);
63
- const stats = await withAbort(stat(validPath), signal);
64
- assertPatchTargetSizeWithinLimit(validPath, stats.size, MAX_TEXT_FILE_SIZE);
65
- const { content } = await readFileWithStats(filePath, validPath, stats, {
66
- encoding: 'utf-8',
67
- maxSize: MAX_TEXT_FILE_SIZE,
68
- skipBinary: true,
69
- ...(signal ? { signal } : {}),
70
- });
71
- const patched = applyPatch(content, diff, {
72
- fuzzFactor: options.fuzzFactor,
73
- autoConvertLineEndings: options.autoConvertLineEndings,
74
- });
75
- if (patched === false) {
76
- return {
77
- path: validPath,
78
- applied: false,
79
- error: buildStructuredError(new McpError(ErrorCode.INVALID_INPUT, 'Patch application failed', validPath), ErrorCode.INVALID_INPUT, validPath),
80
- };
81
- }
82
- if (patched === content) {
83
- return {
84
- path: validPath,
85
- applied: false,
86
- error: buildStructuredError(new McpError(ErrorCode.INVALID_INPUT, 'Patch had no effect', validPath), ErrorCode.INVALID_INPUT, validPath),
87
- };
88
- }
89
- const patchStats = countStructuredPatchStats(diff);
90
- if (!options.dryRun) {
91
- await atomicWriteFile(validPath, patched, { encoding: 'utf-8', signal });
92
- }
93
- return { path: validPath, applied: true, ...patchStats };
94
- }
95
- async function processMultiFilePatch(basePath, parsed, options, signal) {
96
- const validBase = await validateExistingPath(basePath, signal);
97
- const promises = parsed.map((diff) => {
98
- const fileName = extractPatchTargetPath(diff);
99
- if (!fileName) {
100
- return () => Promise.resolve({
101
- path: '<unknown>',
102
- applied: false,
103
- error: buildStructuredError(new McpError(ErrorCode.INVALID_INPUT, 'Missing file name in patch header'), ErrorCode.INVALID_INPUT),
104
- });
105
- }
106
- const filePath = resolve(validBase, fileName);
107
- return async () => {
108
- try {
109
- const result = await applyDiff(filePath, diff, options, signal);
110
- return { ...result, path: fileName };
111
- }
112
- catch (error) {
113
- return {
114
- path: fileName,
115
- applied: false,
116
- error: buildStructuredError(error, ErrorCode.INVALID_INPUT, filePath),
117
- };
118
- }
119
- };
120
- });
121
- const { results } = await processInParallel(promises, (task) => task(), PARALLEL_CONCURRENCY, signal);
122
- const totals = results.reduce((acc, r) => {
123
- if (r.applied) {
124
- acc.applied++;
125
- acc.hunks += r.hunksApplied ?? 0;
126
- acc.added += r.linesAdded ?? 0;
127
- acc.removed += r.linesRemoved ?? 0;
128
- }
129
- return acc;
130
- }, { applied: 0, hunks: 0, added: 0, removed: 0 });
131
- const label = options.dryRun ? ' (dry run)' : '';
132
- if (totals.applied === 0) {
133
- const failedPaths = results
134
- .filter((r) => !r.applied)
135
- .map((r) => r.path)
136
- .join(', ');
137
- throw new McpError(ErrorCode.INVALID_INPUT, `All ${parsed.length} patches failed${label}. Files: ${failedPaths}. Regenerate via diff_files.`);
138
- }
139
- const text = `Applied ${totals.applied}/${parsed.length} file patches${label}`;
140
- if (!options.dryRun) {
141
- Logger.info(`apply_patch: ${basePath} (${totals.applied} file(s), +${totals.added}/-${totals.removed})`);
142
- }
143
- return buildToolResponse(text, {
144
- ok: totals.applied === parsed.length,
145
- path: basePath,
146
- applied: totals.applied > 0,
147
- hunksApplied: totals.hunks,
148
- linesAdded: totals.added,
149
- linesRemoved: totals.removed,
150
- results,
151
- });
152
- }
153
- async function handleApplyPatch(args, signal) {
154
- if (!args.patch.trim()) {
155
- throw new McpError(ErrorCode.INVALID_INPUT, 'Patch content is empty.');
156
- }
157
- const fuzzFactor = args.fuzzFactor ?? 0;
158
- const parsed = parsePatch(args.patch);
159
- const hasHunks = parsed.some((p) => p.hunks.length > 0);
160
- if (!hasHunks) {
161
- throw new McpError(ErrorCode.INVALID_INPUT, 'Patch must include unified hunk headers (@@ -n,n +n,n @@).');
162
- }
163
- const options = {
164
- dryRun: args.dryRun,
165
- fuzzFactor,
166
- autoConvertLineEndings: args.autoConvertLineEndings,
167
- };
168
- if (parsed.length > 1) {
169
- return processMultiFilePatch(args.path, parsed, options, signal);
170
- }
171
- const diff = parsed[0];
172
- if (!diff) {
173
- throw new McpError(ErrorCode.INVALID_INPUT, 'No patch content found.');
174
- }
175
- const result = await applyDiff(args.path, diff, options, signal);
176
- if (!result.applied) {
177
- throw new McpError(ErrorCode.INVALID_INPUT, result.error?.message === 'Patch had no effect'
178
- ? 'Patch had no effect. Content unchanged. Regenerate via diff_files.'
179
- : 'Patch failed. Content may have changed. Regenerate via diff_files. For minor diffs, use fuzzFactor.');
180
- }
181
- const text = args.dryRun
182
- ? 'Dry run successful. Patch can be applied.'
183
- : `Successfully patched ${args.path}`;
184
- if (!args.dryRun) {
185
- Logger.info(`apply_patch: ${args.path} (+${result.linesAdded ?? 0}/-${result.linesRemoved ?? 0})`);
186
- }
187
- return buildToolResponse(text, {
188
- ok: true,
189
- path: result.path,
190
- applied: true,
191
- hunksApplied: result.hunksApplied,
192
- linesAdded: result.linesAdded,
193
- linesRemoved: result.linesRemoved,
194
- });
195
- }
196
- export function registerApplyPatchTool(server, options = {}) {
197
- const handler = (args, ctx) => executeToolWithDiagnostics({
198
- toolName: 'apply_patch',
199
- ctx,
200
- outputSchema: ApplyPatchOutputSchema,
201
- timedSignal: {},
202
- context: { path: args.path },
203
- run: async (signal) => {
204
- const result = await handleApplyPatch(args, signal);
205
- if (!args.dryRun) {
206
- const sc = result.structuredContent;
207
- void ctx.log?.('info', `patch: ${args.path} (+${String(sc.linesAdded ?? 0)}/-${String(sc.linesRemoved ?? 0)})`);
208
- }
209
- return result;
210
- },
211
- onError: (error) => buildToolErrorResponse(error, ErrorCode.UNKNOWN, args.path),
212
- });
213
- registerStandardTool(server, APPLY_PATCH_TOOL, handler, options, {
214
- progressMessage: (args) => {
215
- const name = basename(args.path);
216
- return args.dryRun
217
- ? `${APPLY_PATCH_TOOL.title}: ${name} [dry run]`
218
- : `${APPLY_PATCH_TOOL.title}: ${name}`;
219
- },
220
- completionMessage: (args, result) => {
221
- const name = basename(args.path);
222
- if (result.isError)
223
- return `${APPLY_PATCH_TOOL.title}: ${name} • ${result.errorCode}`;
224
- const sc = result.structuredContent;
225
- if (!sc.ok)
226
- return `${APPLY_PATCH_TOOL.title}: ${name} • failed`;
227
- const added = sc.linesAdded ?? 0;
228
- const removed = sc.linesRemoved ?? 0;
229
- const dry = args.dryRun ? 'dry run ' : '';
230
- if (added > 0 || removed > 0)
231
- return `${APPLY_PATCH_TOOL.title}: ${name} • ${dry} +${added} -${removed}`;
232
- return `${APPLY_PATCH_TOOL.title}: ${name} • ${dry}no changes`;
233
- },
234
- });
235
- }
@@ -1,4 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
3
- export declare const CALCULATE_HASH_TOOL: ToolContract;
4
- export declare function registerCalculateHashTool(server: McpServer, options?: ToolRegistrationOptions): void;