@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,572 +0,0 @@
1
- import { readdir, realpath, stat } from 'node:fs/promises';
2
- import { basename, dirname, isAbsolute, join, parse, resolve, sep, } from 'node:path';
3
- import { z } from 'zod';
4
- import { getAllowedDirectories, isPathWithinDirectories, normalizePath, toPosixPath, } from './lib/paths.js';
5
- import { isRecord } from './lib/utils.js';
6
- import { getSortedToolContracts } from './resources/tool-info.js';
7
- const MAX_COMPLETION_ITEMS = 100;
8
- const COMPLETION_RATE_LIMIT_MS = 100;
9
- const MAX_COMPLETION_CACHE_KEYS = 128;
10
- // WeakMap keyed by McpServer instance so that each HTTP session gets isolated
11
- // rate-limit state. In stdio mode there is a single server; in HTTP mode every
12
- // session creates its own McpServer, so cross-session cache pollution is avoided.
13
- const completionState = new WeakMap();
14
- function getCompletionState(server) {
15
- let state = completionState.get(server);
16
- if (state === undefined) {
17
- state = { lastCallMs: new Map(), lastResult: new Map() };
18
- completionState.set(server, state);
19
- }
20
- return state;
21
- }
22
- function extractTopicCompletions(instructions) {
23
- const headers = [];
24
- for (const line of instructions.split('\n')) {
25
- if (line.startsWith('## ')) {
26
- const header = line.slice(3).trim().toLowerCase();
27
- if (header)
28
- headers.push(header);
29
- }
30
- }
31
- return headers;
32
- }
33
- function extractToolNameCompletions() {
34
- return getSortedToolContracts().map((contract) => contract.name);
35
- }
36
- const PATH_ARGUMENTS = new Set([
37
- 'path',
38
- 'source',
39
- 'destination',
40
- 'original',
41
- 'modified',
42
- 'directory',
43
- 'file',
44
- 'root',
45
- 'cwd',
46
- ]);
47
- const DESTINATION_CONTEXT_KEYS = ['source', 'path', 'cwd', 'root'];
48
- const PRIMARY_PATH_CONTEXT_KEYS = ['path', 'cwd', 'root'];
49
- const DEFAULT_CONTEXT_KEYS = ['path', 'source', 'cwd', 'root'];
50
- function extractEnumValuesFromSchema(argumentName, schema) {
51
- const properties = getInputSchemaProperties(schema);
52
- if (!properties || typeof properties !== 'object')
53
- return undefined;
54
- for (const [key, value] of Object.entries(properties)) {
55
- if (key.toLowerCase() !== argumentName)
56
- continue;
57
- if (value === null || typeof value !== 'object' || !('enum' in value)) {
58
- return undefined;
59
- }
60
- const enumValues = value.enum;
61
- if (!Array.isArray(enumValues))
62
- return undefined;
63
- const stringValues = enumValues.filter((entry) => typeof entry === 'string');
64
- return stringValues.length > 0 ? stringValues : undefined;
65
- }
66
- return undefined;
67
- }
68
- function getInputSchemaProperties(schema) {
69
- const { properties } = z.toJSONSchema(schema, { io: 'input' });
70
- if (!properties || typeof properties !== 'object')
71
- return undefined;
72
- return properties;
73
- }
74
- function intersectEnumValueSets(valueSets) {
75
- const [firstSet, ...restSets] = valueSets;
76
- if (!firstSet)
77
- return [];
78
- return firstSet.filter((value) => restSets.every((candidateSet) => candidateSet.includes(value)));
79
- }
80
- function buildEnumArgumentValues() {
81
- const valuesByArgument = new Map();
82
- for (const contract of getSortedToolContracts()) {
83
- const properties = getInputSchemaProperties(contract.inputSchema);
84
- if (!properties || typeof properties !== 'object')
85
- continue;
86
- for (const key of Object.keys(properties)) {
87
- const normalizedKey = key.toLowerCase();
88
- const values = extractEnumValuesFromSchema(normalizedKey, contract.inputSchema);
89
- if (!values)
90
- continue;
91
- const existing = valuesByArgument.get(normalizedKey) ?? [];
92
- existing.push(values);
93
- valuesByArgument.set(normalizedKey, existing);
94
- }
95
- }
96
- const result = new Map();
97
- for (const [argumentName, valueSets] of valuesByArgument) {
98
- const values = valueSets.length === 1
99
- ? (valueSets[0] ?? [])
100
- : intersectEnumValueSets(valueSets);
101
- if (values.length > 0) {
102
- result.set(argumentName, values);
103
- }
104
- }
105
- return result;
106
- }
107
- function isPathLikeArgumentName(argName) {
108
- return (PATH_ARGUMENTS.has(argName) ||
109
- argName.endsWith('paths') ||
110
- argName.endsWith('path') ||
111
- argName.endsWith('files') ||
112
- argName.endsWith('file') ||
113
- argName.endsWith('dirs') ||
114
- argName.endsWith('dir'));
115
- }
116
- function getEnumCompletions(argName, currentValue, enumArgumentValues) {
117
- const values = enumArgumentValues.get(argName);
118
- if (!values)
119
- return undefined;
120
- const prefix = currentValue.toLowerCase();
121
- const filtered = prefix
122
- ? values.filter((v) => v.startsWith(prefix))
123
- : [...values];
124
- return buildCompletionResult(filtered);
125
- }
126
- function isTemplateVariableChar(char) {
127
- const code = char.charCodeAt(0);
128
- const isDigit = code >= 48 && code <= 57;
129
- const isUpper = code >= 65 && code <= 90;
130
- const isLower = code >= 97 && code <= 122;
131
- return isDigit || isUpper || isLower || code === 95;
132
- }
133
- function normalizeTemplateVariable(raw) {
134
- let normalized = '';
135
- for (const char of raw) {
136
- if (isTemplateVariableChar(char)) {
137
- normalized += char.toLowerCase();
138
- }
139
- }
140
- return normalized;
141
- }
142
- function parseResourceReference(value) {
143
- if (!isRecord(value))
144
- return undefined;
145
- if (value['type'] !== 'ref/resource')
146
- return undefined;
147
- const { uri } = value;
148
- if (typeof uri !== 'string')
149
- return undefined;
150
- return { type: 'ref/resource', uri };
151
- }
152
- function extractTemplateVariables(uri) {
153
- const vars = [];
154
- let index = 0;
155
- while (index < uri.length) {
156
- const start = uri.indexOf('{', index);
157
- if (start === -1)
158
- break;
159
- const end = uri.indexOf('}', start + 1);
160
- if (end === -1)
161
- break;
162
- const normalized = normalizeTemplateVariable(uri.slice(start + 1, end));
163
- if (normalized.length > 0)
164
- vars.push(normalized);
165
- index = end + 1;
166
- }
167
- return vars;
168
- }
169
- function isPathArgumentFromReference(argumentName, ref) {
170
- const resourceRef = parseResourceReference(ref);
171
- if (!resourceRef)
172
- return false;
173
- const normalizedArg = argumentName.toLowerCase();
174
- const templateVars = extractTemplateVariables(resourceRef.uri);
175
- if (templateVars.length === 0)
176
- return false;
177
- const matchesVariable = templateVars.includes(normalizedArg);
178
- if (!matchesVariable)
179
- return false;
180
- if (isPathLikeArgumentName(normalizedArg))
181
- return true;
182
- if (resourceRef.uri.toLowerCase().includes('file:///'))
183
- return true;
184
- const uriLooksPathLike = resourceRef.uri.includes('/') &&
185
- (resourceRef.uri.toLowerCase().includes('path') ||
186
- resourceRef.uri.toLowerCase().includes('file') ||
187
- resourceRef.uri.toLowerCase().includes('dir') ||
188
- resourceRef.uri.toLowerCase().includes('root') ||
189
- resourceRef.uri.toLowerCase().includes('cwd'));
190
- return uriLooksPathLike;
191
- }
192
- function extractContextArguments(value) {
193
- if (!isRecord(value))
194
- return undefined;
195
- const context = value['arguments'];
196
- if (!isRecord(context))
197
- return undefined;
198
- const normalized = {};
199
- let count = 0;
200
- for (const [key, entryValue] of Object.entries(context)) {
201
- if (typeof entryValue !== 'string')
202
- continue;
203
- normalized[key.toLowerCase()] = entryValue;
204
- count += 1;
205
- }
206
- if (count === 0)
207
- return undefined;
208
- return normalized;
209
- }
210
- function serializeCompletionRef(ref) {
211
- if (!isRecord(ref) || typeof ref['type'] !== 'string') {
212
- return { type: 'unknown' };
213
- }
214
- if (ref['type'] === 'ref/prompt' && typeof ref['name'] === 'string') {
215
- return { type: ref['type'], name: ref['name'] };
216
- }
217
- if (ref['type'] === 'ref/resource' && typeof ref['uri'] === 'string') {
218
- return { type: ref['type'], uri: ref['uri'] };
219
- }
220
- return { type: ref['type'] };
221
- }
222
- function serializeContextArguments(contextArguments) {
223
- if (!contextArguments)
224
- return [];
225
- return Object.entries(contextArguments).sort(([left], [right]) => left.localeCompare(right));
226
- }
227
- function buildCompletionCacheKey(params) {
228
- return JSON.stringify({
229
- argumentName: params.argumentName.toLowerCase(),
230
- value: params.value,
231
- ref: serializeCompletionRef(params.ref),
232
- contextArguments: serializeContextArguments(params.contextArguments),
233
- });
234
- }
235
- function rememberCompletionCacheValue(cache, key, value) {
236
- if (cache.has(key)) {
237
- cache.delete(key);
238
- }
239
- cache.set(key, value);
240
- while (cache.size > MAX_COMPLETION_CACHE_KEYS) {
241
- const oldest = cache.keys().next();
242
- if (oldest.done)
243
- break;
244
- cache.delete(oldest.value);
245
- }
246
- }
247
- function hasTrailingSeparator(value) {
248
- return value.endsWith(sep) || value.endsWith('/') || value.endsWith('\\');
249
- }
250
- function isAbsolutePathInput(value) {
251
- return (isAbsolute(value) ||
252
- /^[A-Za-z]:[\\/]/u.test(value) ||
253
- value.startsWith('\\\\'));
254
- }
255
- function resolveFromBase(base, rawValue, trailingSeparator) {
256
- const normalizedValue = normalizePath(resolve(base, rawValue));
257
- if (trailingSeparator) {
258
- return { searchDir: normalizedValue, prefix: '' };
259
- }
260
- return {
261
- searchDir: dirname(normalizedValue),
262
- prefix: basename(normalizedValue),
263
- };
264
- }
265
- function resolveNamedRootContext(currentValue, allowed) {
266
- const parsed = parseNamedRootInput(currentValue);
267
- if (!parsed)
268
- return undefined;
269
- const root = findAllowedRootByName(parsed.rootName, allowed);
270
- if (!root)
271
- return undefined;
272
- const trailingSeparator = hasTrailingSeparator(currentValue);
273
- return resolveFromBase(root, parsed.remainder, trailingSeparator);
274
- }
275
- function resolveNamedRootPath(value, allowed) {
276
- const parsed = parseNamedRootInput(value);
277
- if (!parsed)
278
- return undefined;
279
- const root = findAllowedRootByName(parsed.rootName, allowed);
280
- if (!root)
281
- return undefined;
282
- return normalizePath(resolve(root, parsed.remainder));
283
- }
284
- function parseNamedRootInput(value) {
285
- const normalizedInput = toPosixPath(value);
286
- const [rootName, ...rest] = normalizedInput.split('/');
287
- if (!rootName)
288
- return undefined;
289
- return { rootName, remainder: rest.join(sep) };
290
- }
291
- function findAllowedRootByName(rootName, allowed) {
292
- const normalizedRootName = rootName.toLowerCase();
293
- return allowed.find((candidate) => basename(candidate).toLowerCase() === normalizedRootName);
294
- }
295
- function chooseContextKeys(argumentName) {
296
- const normalized = argumentName.toLowerCase();
297
- if (normalized === 'destination') {
298
- return [...DESTINATION_CONTEXT_KEYS];
299
- }
300
- if (normalized === 'path' ||
301
- normalized === 'source' ||
302
- normalized === 'original' ||
303
- normalized === 'modified' ||
304
- normalized === 'file') {
305
- return [...PRIMARY_PATH_CONTEXT_KEYS];
306
- }
307
- return [...DEFAULT_CONTEXT_KEYS];
308
- }
309
- function hasContextArguments(contextArguments) {
310
- return (contextArguments !== undefined && Object.keys(contextArguments).length > 0);
311
- }
312
- function resolveContextCandidatePath(candidate, allowed) {
313
- if (isAbsolutePathInput(candidate)) {
314
- return normalizePath(candidate);
315
- }
316
- if (allowed.length === 1) {
317
- const base = allowed[0];
318
- if (!base)
319
- return undefined;
320
- return normalizePath(resolve(base, candidate));
321
- }
322
- return resolveNamedRootPath(candidate, allowed);
323
- }
324
- async function toAllowedContextDirectory(resolved, allowed) {
325
- const parent = dirname(resolved);
326
- if (await isAllowedCompletionDirectory(resolved, allowed)) {
327
- return resolved;
328
- }
329
- return (await isAllowedCompletionDirectory(parent, allowed))
330
- ? parent
331
- : undefined;
332
- }
333
- async function isAllowedCompletionDirectory(path, allowed) {
334
- if (!isPathWithinDirectories(path, allowed))
335
- return false;
336
- try {
337
- const [stats, resolvedRealPath] = await Promise.all([
338
- stat(path),
339
- realpath(path),
340
- ]);
341
- if (!stats.isDirectory())
342
- return false;
343
- return isPathWithinDirectories(normalizePath(resolvedRealPath), allowed);
344
- }
345
- catch {
346
- return false;
347
- }
348
- }
349
- async function resolveContextBaseDirectory(argumentName, contextArguments, allowed) {
350
- if (!hasContextArguments(contextArguments)) {
351
- return undefined;
352
- }
353
- const keys = chooseContextKeys(argumentName);
354
- for (const key of keys) {
355
- const candidate = contextArguments[key];
356
- if (!candidate || candidate.trim().length === 0)
357
- continue;
358
- const resolved = resolveContextCandidatePath(candidate, allowed);
359
- if (!resolved)
360
- continue;
361
- const baseDirectory = await toAllowedContextDirectory(resolved, allowed);
362
- if (baseDirectory)
363
- return baseDirectory;
364
- }
365
- return undefined;
366
- }
367
- function withDirectorySeparator(value) {
368
- return value.endsWith(sep) ? value : `${value}${sep}`;
369
- }
370
- function buildCompletionResult(values) {
371
- return {
372
- values: values.slice(0, MAX_COMPLETION_ITEMS),
373
- total: values.length,
374
- hasMore: values.length > MAX_COMPLETION_ITEMS,
375
- };
376
- }
377
- function buildCompletionResponse(result) {
378
- return { completion: result };
379
- }
380
- function sortCompletionMatches(matches) {
381
- matches.sort((left, right) => {
382
- const leftIsDir = left.endsWith(sep);
383
- const rightIsDir = right.endsWith(sep);
384
- if (leftIsDir && !rightIsDir)
385
- return -1;
386
- if (!leftIsDir && rightIsDir)
387
- return 1;
388
- return left.localeCompare(right);
389
- });
390
- }
391
- function mergeCompletionMatches(...matchGroups) {
392
- const uniqueMatches = new Set();
393
- for (const group of matchGroups) {
394
- for (const match of group) {
395
- uniqueMatches.add(match);
396
- }
397
- }
398
- const merged = [...uniqueMatches];
399
- sortCompletionMatches(merged);
400
- return merged;
401
- }
402
- function getRootPrefix(currentValue) {
403
- const normalizedInput = toPosixPath(currentValue);
404
- return (normalizedInput.split('/')[0] ?? '').toLowerCase();
405
- }
406
- function collectAllowedRoots(allowed, predicate) {
407
- const matches = [];
408
- for (const root of allowed) {
409
- if (predicate(root)) {
410
- matches.push(withDirectorySeparator(root));
411
- }
412
- }
413
- return matches;
414
- }
415
- function getSearchContext(currentValue, allowed, contextBase) {
416
- const trailingSeparator = hasTrailingSeparator(currentValue);
417
- if (isAbsolutePathInput(currentValue)) {
418
- return resolveFromBase(parse(currentValue).root || sep, currentValue, trailingSeparator);
419
- }
420
- const namedRootContext = resolveNamedRootContext(currentValue, allowed);
421
- if (namedRootContext) {
422
- return namedRootContext;
423
- }
424
- if (contextBase) {
425
- if (currentValue.length === 0) {
426
- return { searchDir: contextBase, prefix: '' };
427
- }
428
- return resolveFromBase(contextBase, currentValue, trailingSeparator);
429
- }
430
- if (allowed.length === 1) {
431
- const base = allowed[0];
432
- if (base) {
433
- return resolveFromBase(base, currentValue, trailingSeparator);
434
- }
435
- }
436
- return undefined;
437
- }
438
- async function findMatchesInDirectory(searchDir, prefix, allowed) {
439
- const matches = [];
440
- if (!(await isAllowedCompletionDirectory(searchDir, allowed))) {
441
- return matches;
442
- }
443
- try {
444
- const entries = await readdir(searchDir, { withFileTypes: true });
445
- const lowerPrefix = prefix.toLowerCase();
446
- for (const entry of entries) {
447
- if (entry.name.toLowerCase().startsWith(lowerPrefix)) {
448
- const fullPath = join(searchDir, entry.name);
449
- const isDir = entry.isDirectory();
450
- matches.push(isDir ? `${fullPath}${sep}` : fullPath);
451
- }
452
- }
453
- }
454
- catch {
455
- // Access denied or not found, ignore
456
- }
457
- return matches;
458
- }
459
- function findRootPrefixMatches(currentValue, allowed) {
460
- const rootPrefix = getRootPrefix(currentValue);
461
- if (!rootPrefix) {
462
- return collectAllowedRoots(allowed, () => true);
463
- }
464
- return collectAllowedRoots(allowed, (root) => basename(root).toLowerCase().startsWith(rootPrefix));
465
- }
466
- function findMatchingRoots(searchDir, prefix, allowed) {
467
- const lowerPrefix = prefix.toLowerCase();
468
- const normalizedSearchDir = normalizePath(searchDir);
469
- return collectAllowedRoots(allowed, (root) => {
470
- const rootDir = dirname(root);
471
- // Check if root is a direct child of searchDir
472
- if (normalizePath(rootDir) !== normalizedSearchDir)
473
- return false;
474
- return basename(root).toLowerCase().startsWith(lowerPrefix);
475
- });
476
- }
477
- async function getPathCompletions(currentValue, options = {}) {
478
- const allowed = getAllowedDirectories();
479
- try {
480
- const contextBase = await resolveContextBaseDirectory(options.argumentName ?? '', options.contextArguments, allowed);
481
- // If no value and no context base, suggest roots.
482
- if (!currentValue && !contextBase) {
483
- return buildCompletionResult(allowed);
484
- }
485
- const context = getSearchContext(currentValue, allowed, contextBase);
486
- if (!context) {
487
- return buildCompletionResult(findRootPrefixMatches(currentValue, allowed));
488
- }
489
- const { searchDir, prefix } = context;
490
- const dirMatches = await findMatchesInDirectory(searchDir, prefix, allowed);
491
- const rootMatches = findMatchingRoots(searchDir, prefix, allowed);
492
- return buildCompletionResult(mergeCompletionMatches(dirMatches, rootMatches));
493
- }
494
- catch {
495
- return { values: [] };
496
- }
497
- }
498
- function handleTopicAndToolCompletions(ref, argName, argumentValue, topicValues, toolNameValues) {
499
- if (!isRecord(ref))
500
- return undefined;
501
- const currentValue = argumentValue.toLowerCase();
502
- if (ref['type'] === 'ref/prompt' && argName === 'topic') {
503
- const filtered = currentValue
504
- ? topicValues.filter((v) => v.startsWith(currentValue))
505
- : topicValues;
506
- return buildCompletionResponse(buildCompletionResult(filtered));
507
- }
508
- if (ref['type'] === 'ref/prompt' &&
509
- ref['name'] === 'get-tool-help' &&
510
- argName === 'name') {
511
- const filtered = currentValue
512
- ? toolNameValues.filter((value) => value.startsWith(currentValue))
513
- : toolNameValues;
514
- return buildCompletionResponse(buildCompletionResult(filtered));
515
- }
516
- if (ref['type'] === 'ref/resource' &&
517
- ref['uri'] === 'internal://tool-info/{name}' &&
518
- argName === 'name') {
519
- const filtered = currentValue
520
- ? toolNameValues.filter((value) => value.startsWith(currentValue))
521
- : toolNameValues;
522
- return buildCompletionResponse(buildCompletionResult(filtered));
523
- }
524
- return undefined;
525
- }
526
- export function registerCompletions(server, instructions = '') {
527
- const topicValues = extractTopicCompletions(instructions);
528
- const toolNameValues = extractToolNameCompletions();
529
- const enumArgumentValues = buildEnumArgumentValues();
530
- server.server.setRequestHandler('completion/complete', async (request) => {
531
- const { params } = request;
532
- const { argument, ref } = params;
533
- const argName = argument.name.toLowerCase();
534
- const predef = handleTopicAndToolCompletions(ref, argName, argument.value, topicValues, toolNameValues);
535
- if (predef)
536
- return predef;
537
- const enumResult = getEnumCompletions(argName, argument.value, enumArgumentValues);
538
- if (enumResult) {
539
- return buildCompletionResponse(enumResult);
540
- }
541
- const isPathArg = isPathLikeArgumentName(argName) ||
542
- isPathArgumentFromReference(argName, ref);
543
- if (!isPathArg) {
544
- return buildCompletionResponse(buildCompletionResult([]));
545
- }
546
- const contextArguments = extractContextArguments(params.context);
547
- const cacheKey = buildCompletionCacheKey({
548
- argumentName: argName,
549
- value: argument.value,
550
- ref,
551
- ...(contextArguments ? { contextArguments } : {}),
552
- });
553
- const now = Date.now();
554
- const sessionState = getCompletionState(server);
555
- const lastCallMs = sessionState.lastCallMs.get(cacheKey) ?? 0;
556
- if (now - lastCallMs < COMPLETION_RATE_LIMIT_MS) {
557
- const lastResult = sessionState.lastResult.get(cacheKey);
558
- if (lastResult) {
559
- return buildCompletionResponse(lastResult);
560
- }
561
- return buildCompletionResponse(buildCompletionResult([]));
562
- }
563
- rememberCompletionCacheValue(sessionState.lastCallMs, cacheKey, now);
564
- const { value } = argument;
565
- const completions = await getPathCompletions(value, {
566
- argumentName: argName,
567
- ...(contextArguments ? { contextArguments } : {}),
568
- });
569
- rememberCompletionCacheValue(sessionState.lastResult, cacheKey, completions);
570
- return buildCompletionResponse(completions);
571
- });
572
- }
package/dist/config.d.ts DELETED
@@ -1,119 +0,0 @@
1
- export type FileType = 'file' | 'directory' | 'symlink' | 'other';
2
- export interface FileInfo {
3
- readonly name: string;
4
- readonly path: string;
5
- readonly type: FileType;
6
- readonly size: number;
7
- readonly tokenEstimate?: number;
8
- readonly created: Date;
9
- readonly modified: Date;
10
- readonly accessed: Date;
11
- readonly permissions: string;
12
- readonly isHidden: boolean;
13
- readonly mimeType?: string;
14
- readonly symlinkTarget?: string;
15
- }
16
- export interface DirectoryEntry {
17
- readonly name: string;
18
- readonly path: string;
19
- readonly relativePath: string;
20
- readonly type: FileType;
21
- readonly size?: number;
22
- readonly modified?: Date;
23
- readonly symlinkTarget?: string;
24
- }
25
- export interface ListDirectoryResult {
26
- readonly path: string;
27
- readonly entries: readonly DirectoryEntry[];
28
- readonly summary: {
29
- readonly totalEntries: number;
30
- readonly entriesScanned?: number;
31
- readonly entriesVisible?: number;
32
- readonly totalFiles: number;
33
- readonly totalDirectories: number;
34
- readonly maxDepthReached: number;
35
- readonly truncated: boolean;
36
- readonly stoppedReason?: 'maxEntries' | 'aborted';
37
- readonly skippedInaccessible: number;
38
- readonly symlinksNotFollowed: number;
39
- };
40
- }
41
- export interface SearchResult {
42
- readonly path: string;
43
- readonly type: FileType;
44
- readonly size?: number;
45
- readonly modified?: Date;
46
- }
47
- export interface SearchFilesResult {
48
- readonly basePath: string;
49
- readonly pattern: string;
50
- readonly results: readonly SearchResult[];
51
- readonly summary: {
52
- readonly matched: number;
53
- readonly truncated: boolean;
54
- readonly skippedInaccessible: number;
55
- readonly filesScanned: number;
56
- readonly stoppedReason?: 'maxResults' | 'maxFiles' | 'timeout';
57
- };
58
- }
59
- export interface ContentMatch {
60
- readonly file: string;
61
- readonly line: number;
62
- readonly content: string;
63
- readonly contextBefore?: readonly string[];
64
- readonly contextAfter?: readonly string[];
65
- readonly matchCount: number;
66
- }
67
- export interface SearchContentResult {
68
- readonly basePath: string;
69
- readonly pattern: string;
70
- readonly filePattern: string;
71
- readonly matches: readonly ContentMatch[];
72
- readonly summary: {
73
- readonly filesScanned: number;
74
- readonly filesMatched: number;
75
- readonly matches: number;
76
- readonly truncated: boolean;
77
- readonly skippedTooLarge: number;
78
- readonly skippedBinary: number;
79
- readonly skippedInaccessible: number;
80
- readonly stoppedReason?: 'maxResults' | 'maxFiles' | 'timeout';
81
- };
82
- }
83
- export interface MultipleFileInfoResult {
84
- readonly path: string;
85
- readonly info?: FileInfo;
86
- readonly error?: Error;
87
- }
88
- export interface GetMultipleFileInfoResult {
89
- readonly results: readonly MultipleFileInfoResult[];
90
- readonly summary: {
91
- readonly total: number;
92
- readonly succeeded: number;
93
- readonly failed: number;
94
- readonly totalSize: number;
95
- };
96
- }
97
- export declare const ErrorCode: {
98
- readonly ACCESS_DENIED: "ACCESS_DENIED";
99
- readonly NOT_FOUND: "NOT_FOUND";
100
- readonly NOT_FILE: "NOT_FILE";
101
- readonly NOT_DIRECTORY: "NOT_DIRECTORY";
102
- readonly TOO_LARGE: "TOO_LARGE";
103
- readonly TIMEOUT: "TIMEOUT";
104
- readonly CANCELLED: "CANCELLED";
105
- readonly INVALID_PATTERN: "INVALID_PATTERN";
106
- readonly INVALID_INPUT: "INVALID_INPUT";
107
- readonly PERMISSION_DENIED: "PERMISSION_DENIED";
108
- readonly SYMLINK_NOT_ALLOWED: "SYMLINK_NOT_ALLOWED";
109
- readonly UNKNOWN: "UNKNOWN";
110
- };
111
- export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
112
- export declare function formatBytes(bytes: number): string;
113
- export declare function joinLines(lines: readonly string[]): string;
114
- interface OperationSummary {
115
- truncated?: boolean;
116
- truncatedReason?: string;
117
- }
118
- export declare function formatOperationSummary(summary: OperationSummary): string;
119
- export {};