@j0hanz/filesystem-mcp 1.14.0 → 1.15.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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -71
  3. package/dist/cli.js +2 -2
  4. package/dist/completions.js +54 -51
  5. package/dist/config.d.ts +13 -13
  6. package/dist/config.js +12 -12
  7. package/dist/index.js +1 -1
  8. package/dist/lib/abort.d.ts +7 -0
  9. package/dist/lib/abort.js +81 -0
  10. package/dist/lib/constants.d.ts +3 -1
  11. package/dist/lib/constants.js +8 -2
  12. package/dist/lib/errors.d.ts +7 -3
  13. package/dist/lib/errors.js +59 -39
  14. package/dist/lib/file-operations/core.d.ts +3 -3
  15. package/dist/lib/file-operations/core.js +23 -20
  16. package/dist/lib/file-operations/metadata.d.ts +2 -2
  17. package/dist/lib/file-operations/metadata.js +60 -19
  18. package/dist/lib/file-operations/search.js +83 -84
  19. package/dist/lib/file-operations/traversal.js +13 -15
  20. package/dist/lib/fs-helpers.d.ts +3 -10
  21. package/dist/lib/fs-helpers.js +20 -98
  22. package/dist/lib/globs.js +1 -1
  23. package/dist/lib/logger.d.ts +28 -0
  24. package/dist/lib/logger.js +91 -0
  25. package/dist/lib/observability.d.ts +7 -0
  26. package/dist/lib/observability.js +19 -9
  27. package/dist/lib/paths.js +55 -55
  28. package/dist/lib/resource-store.js +4 -4
  29. package/dist/lib/utils.d.ts +0 -12
  30. package/dist/lib/utils.js +0 -13
  31. package/dist/resources/generated-instructions.js +40 -31
  32. package/dist/resources/tool-catalog.js +32 -26
  33. package/dist/resources/tool-info.js +34 -29
  34. package/dist/resources/workflows.js +39 -18
  35. package/dist/resources.d.ts +1 -1
  36. package/dist/resources.js +4 -4
  37. package/dist/schemas.d.ts +66 -66
  38. package/dist/schemas.js +21 -44
  39. package/dist/server/bootstrap.d.ts +12 -11
  40. package/dist/server/bootstrap.js +95 -86
  41. package/dist/server/roots-manager.d.ts +5 -2
  42. package/dist/server/roots-manager.js +8 -6
  43. package/dist/server/task-store.d.ts +10 -0
  44. package/dist/server/task-store.js +73 -0
  45. package/dist/tools/apply-patch.js +26 -18
  46. package/dist/tools/calculate-hash.js +11 -22
  47. package/dist/tools/create-directory.js +10 -8
  48. package/dist/tools/delete-file.js +17 -15
  49. package/dist/tools/diff-files.js +15 -15
  50. package/dist/tools/edit-file.js +7 -4
  51. package/dist/tools/list-directory.js +6 -6
  52. package/dist/tools/move-file.js +98 -81
  53. package/dist/tools/read-multiple.js +4 -4
  54. package/dist/tools/read.js +5 -5
  55. package/dist/tools/replace-in-files.js +20 -23
  56. package/dist/tools/roots.js +1 -1
  57. package/dist/tools/search-content.js +32 -41
  58. package/dist/tools/search-files.js +54 -41
  59. package/dist/tools/shared.d.ts +3 -0
  60. package/dist/tools/shared.js +70 -28
  61. package/dist/tools/stat-many.js +11 -7
  62. package/dist/tools/stat.js +4 -4
  63. package/dist/tools/task-support.d.ts +10 -9
  64. package/dist/tools/task-support.js +94 -23
  65. package/dist/tools/tree.js +3 -3
  66. package/dist/tools/write-file.js +10 -7
  67. package/package.json +9 -9
@@ -63,14 +63,14 @@ export function buildCoreContextPack() {
63
63
  const annotations = e.annotations ? ` ${e.annotations.join(' ')}` : '';
64
64
  return `| \`${e.name}\` | ${e.description}${annotations} |`;
65
65
  });
66
- return `<core_context>\n| Tool | Purpose |\n|------|---------|\n${rows.join('\n')}\n</core_context>`;
66
+ return `## Tool Summary\n\n| Tool | Purpose |\n|------|---------|\n${rows.join('\n')}`;
67
67
  }
68
68
  export function getSharedConstraints() {
69
69
  return [
70
- 'Use allowed roots only (from CLI negotiation).',
71
- 'Sensitive paths are denylisted by default.',
72
- `Limits enforced: max file size ${Math.floor(MAX_TEXT_FILE_SIZE / 1024 / 1024)}MB; search caps ${MAX_SEARCH_RESULTS} files, ${DEFAULT_SEARCH_CONTENT_RESULTS} content matches.`,
73
- 'If response includes `resourceUri`, call `resources/read` immediately — cached results expire on restart.',
70
+ 'Operate within allowed roots only (negotiated at startup via CLI).',
71
+ 'Sensitive file paths (e.g. .env, *.pem, *id_rsa*) are denied by default.',
72
+ `Enforced limits: max file size ${Math.floor(MAX_TEXT_FILE_SIZE / 1024 / 1024)} MB, file search cap ${MAX_SEARCH_RESULTS} results, content search cap ${DEFAULT_SEARCH_CONTENT_RESULTS} matches.`,
73
+ 'When a response includes `resourceUri`, call `resources/read` immediately — cached results expire on server restart.',
74
74
  ];
75
75
  }
76
76
  function formatTaskSupportLabel(taskSupport) {
@@ -86,6 +86,14 @@ function formatTaskSupportLabel(taskSupport) {
86
86
  function toJsonSchemaObject(schema, io = 'output') {
87
87
  return z.toJSONSchema(schema, { io });
88
88
  }
89
+ function summarizeArrayType(schema) {
90
+ if (Array.isArray(schema.prefixItems) && schema.prefixItems.length > 0) {
91
+ const itemTypes = schema.prefixItems.map(summarizeSchemaType).join(', ');
92
+ return `tuple<${itemTypes}>`;
93
+ }
94
+ const itemType = schema.items ? summarizeSchemaType(schema.items) : 'unknown';
95
+ return `array<${itemType}>`;
96
+ }
89
97
  function summarizeSchemaType(schema) {
90
98
  if (Array.isArray(schema.enum) && schema.enum.length > 0) {
91
99
  return `enum(${schema.enum.map((value) => JSON.stringify(value)).join(', ')})`;
@@ -100,14 +108,7 @@ function summarizeSchemaType(schema) {
100
108
  return schema.oneOf.map(summarizeSchemaType).join(' | ');
101
109
  }
102
110
  if (schema.type === 'array') {
103
- if (Array.isArray(schema.prefixItems) && schema.prefixItems.length > 0) {
104
- const itemTypes = schema.prefixItems.map(summarizeSchemaType).join(', ');
105
- return `tuple<${itemTypes}>`;
106
- }
107
- const itemType = schema.items
108
- ? summarizeSchemaType(schema.items)
109
- : 'unknown';
110
- return `array<${itemType}>`;
111
+ return summarizeArrayType(schema);
111
112
  }
112
113
  if (schema.type === 'object' && schema.additionalProperties === false) {
113
114
  return 'object (strict)';
@@ -117,16 +118,23 @@ function summarizeSchemaType(schema) {
117
118
  }
118
119
  return 'unknown';
119
120
  }
121
+ function formatFieldLabel(label) {
122
+ return label
123
+ .split('_')
124
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
125
+ .join(' ');
126
+ }
120
127
  function buildSchemaFieldLines(label, schema) {
128
+ const heading = `**${formatFieldLabel(label)}:**`;
121
129
  if (!schema) {
122
- return [`<${label}>`, '- none', `</${label}>`];
130
+ return [heading, '- none'];
123
131
  }
124
132
  const io = label === 'input_fields' ? 'input' : 'output';
125
133
  const jsonSchema = toJsonSchemaObject(schema, io);
126
134
  const properties = jsonSchema.properties ?? {};
127
135
  const required = new Set(jsonSchema.required ?? []);
128
136
  const fieldNames = Object.keys(properties);
129
- const lines = [`<${label}>`];
137
+ const lines = [heading];
130
138
  if (typeof jsonSchema.description === 'string' &&
131
139
  jsonSchema.description.length > 0) {
132
140
  lines.push(`- Schema constraints: ${jsonSchema.description}`);
@@ -135,7 +143,7 @@ function buildSchemaFieldLines(label, schema) {
135
143
  lines.push('- Unknown fields are rejected (`additionalProperties: false`).');
136
144
  }
137
145
  if (fieldNames.length === 0) {
138
- lines.push('- object with no fields', `</${label}>`);
146
+ lines.push('- object with no fields');
139
147
  return lines;
140
148
  }
141
149
  lines.push(...fieldNames.map((fieldName) => {
@@ -147,7 +155,7 @@ function buildSchemaFieldLines(label, schema) {
147
155
  ? fieldSchema.description
148
156
  : 'No description.';
149
157
  return `- ${fieldName} (${type}, ${requiredLabel}): ${description}`;
150
- }), `</${label}>`);
158
+ }));
151
159
  return lines;
152
160
  }
153
161
  export function buildToolInfo(name) {
@@ -165,37 +173,34 @@ export function buildToolInfo(name) {
165
173
  if (contract.annotations?.openWorldHint)
166
174
  annotationLines.push('- openWorldHint: true');
167
175
  const lines = [
168
- `<tool_info name="${entry.name}">`,
169
- `## ${entry.name}`,
176
+ `---`,
177
+ `# ${entry.name}`,
170
178
  '',
171
- `Title: ${entry.title}`,
172
- `Description: ${entry.description}`,
179
+ `**Title:** ${entry.title}`,
180
+ `**Description:** ${entry.description}`,
173
181
  '',
174
- '<execution>',
182
+ '**Execution:**',
175
183
  `- taskSupport: ${formatTaskSupportLabel(contract.taskSupport)}`,
176
- '</execution>',
177
184
  '',
178
185
  ...buildSchemaFieldLines('input_fields', contract.inputSchema),
179
186
  '',
180
187
  ...buildSchemaFieldLines('output_fields', contract.outputSchema),
181
188
  ];
182
189
  if (annotationLines.length > 0) {
183
- lines.push('', '<annotations>', ...annotationLines, '</annotations>');
190
+ lines.push('', '**Behavior:**', ...annotationLines);
184
191
  }
185
192
  if (entry.nuances && entry.nuances.length > 0) {
186
- lines.push('', '<nuances>');
193
+ lines.push('', '**Usage Notes:**');
187
194
  for (const nuance of entry.nuances) {
188
195
  lines.push(`- ${nuance}`);
189
196
  }
190
- lines.push('</nuances>');
191
197
  }
192
198
  if (entry.gotchas && entry.gotchas.length > 0) {
193
- lines.push('', '<gotchas>');
199
+ lines.push('', '**Warnings:**');
194
200
  for (const gotcha of entry.gotchas) {
195
201
  lines.push(`- ${gotcha}`);
196
202
  }
197
- lines.push('</gotchas>');
198
203
  }
199
- lines.push('</tool_info>');
204
+ lines.push('---');
200
205
  return lines.join('\n');
201
206
  }
@@ -18,22 +18,43 @@ export function buildWorkflowGuide() {
18
18
  'rm',
19
19
  'mkdir',
20
20
  ]));
21
- return `<workflows>
22
- ### A: EXPLORE — directory layout or file content
23
- 1. ${exploreTools}.
24
- > **Strict:** Resolve paths first. Never guess.
25
-
26
- ### B: SEARCH — files by pattern or content
27
- 1. ${searchTools}.
28
- > **Strict:** Content search with \`grep\`, not \`find\`.
29
-
30
- ### C: EDIT — modify files or layout
31
- 1. ${editTools}.
32
- 2. Use \`edit\` for targeted changes, \`search_and_replace\` for bulk changes, and \`mv\`/\`rm\`/\`mkdir\` for layout updates.
33
- > **Strict:** Confirm destructive ops (\`write\`, \`mv\`, \`rm\`, bulk replace).
34
-
35
- ### D: PATCH — apply unified diffs
36
- 1. \`diff_files\` → \`apply_patch(dryRun:true)\` → \`apply_patch\`.
37
- > **Tip:** Feed \`diff_files\` output directly. Multi-file patches: \`path\` = base dir, results per file.
38
- </workflows>`;
21
+ return `## Workflows
22
+
23
+ ### A: Explore — Discover directory layout or file content
24
+
25
+ **Tools:** ${exploreTools}
26
+
27
+ 1. Start with \`roots\` to identify allowed directories.
28
+ 2. Use \`ls\` or \`tree\` to map structure, then \`stat\` or \`read\` for details.
29
+
30
+ > Resolve paths before acting. Never assume a path exists.
31
+
32
+ ### B: Search — Find files by name or content
33
+
34
+ **Tools:** ${searchTools}
35
+
36
+ 1. Use \`find\` for file name/glob matching.
37
+ 2. Use \`grep\` for content search (supports regex).
38
+ 3. Use \`read\` to inspect matched files.
39
+
40
+ > Use \`grep\` for content, \`find\` for filenames. Do not conflate them.
41
+
42
+ ### C: Edit — Modify files or directory layout
43
+
44
+ **Tools:** ${editTools}
45
+
46
+ 1. Use \`edit\` for targeted single-file changes (exact text match → replace).
47
+ 2. Use \`search_and_replace\` for bulk changes across multiple files.
48
+ 3. Use \`mv\`, \`rm\`, \`mkdir\` for structural changes.
49
+
50
+ > Confirm destructive operations (\`write\`, \`mv\`, \`rm\`, bulk replace) before executing.
51
+
52
+ ### D: Patch — Generate and apply unified diffs
53
+
54
+ 1. Generate: \`diff_files(original, modified)\`.
55
+ 2. Validate: \`apply_patch(patch, dryRun: true)\`.
56
+ 3. Apply: \`apply_patch(patch)\`.
57
+
58
+ > Multi-file patches: set \`path\` to the base directory. Results are reported per file.
59
+ `;
39
60
  }
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { ResourceStore } from './lib/resource-store.js';
3
3
  import { type IconInfo } from './tools/shared.js';
4
4
  export declare function registerInstructionResource(server: McpServer, instructions: string, iconInfo?: IconInfo): void;
package/dist/resources.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { ErrorCode, McpError } from './lib/errors.js';
3
3
  import { globalMetrics } from './lib/observability.js';
4
4
  import { buildToolCatalog } from './resources/tool-catalog.js';
@@ -104,7 +104,7 @@ export function registerResultResources(server, store, iconInfo) {
104
104
  }, iconInfo), (uri, variables) => {
105
105
  const { id } = variables;
106
106
  if (typeof id !== 'string' || id.length === 0) {
107
- throw new McpError(ErrorCode.E_NOT_FOUND, 'Cached result has expired — re-run the tool to regenerate.');
107
+ throw new McpError(ErrorCode.NOT_FOUND, 'Cached result expired. Re-run the tool to regenerate.');
108
108
  }
109
109
  const entry = store.getText(uri.toString());
110
110
  return {
@@ -130,11 +130,11 @@ export function registerToolInfoResource(server, iconInfo) {
130
130
  }, iconInfo), (uri, variables) => {
131
131
  const { name } = variables;
132
132
  if (typeof name !== 'string' || name.length === 0) {
133
- throw new McpError(ErrorCode.E_INVALID_INPUT, 'Tool name is required');
133
+ throw new McpError(ErrorCode.INVALID_INPUT, 'Tool name is required');
134
134
  }
135
135
  const content = buildToolInfo(name);
136
136
  if (content === undefined) {
137
- throw new McpError(ErrorCode.E_INVALID_INPUT, `Tool not found: ${name}`);
137
+ throw new McpError(ErrorCode.INVALID_INPUT, `Tool not found: ${name}`);
138
138
  }
139
139
  return {
140
140
  contents: [
package/dist/schemas.d.ts CHANGED
@@ -22,9 +22,9 @@ export declare const ListDirectoryInputSchema: z.ZodObject<{
22
22
  maxDepth: z.ZodOptional<z.ZodInt>;
23
23
  maxEntries: z.ZodDefault<z.ZodOptional<z.ZodInt>>;
24
24
  sortBy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
25
- name: "name";
26
- size: "size";
27
25
  type: "type";
26
+ size: "size";
27
+ name: "name";
28
28
  modified: "modified";
29
29
  }>>>;
30
30
  pattern: z.ZodOptional<z.ZodString>;
@@ -39,8 +39,8 @@ export declare const SearchFilesInputSchema: z.ZodObject<{
39
39
  includeIgnored: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
40
40
  includeHidden: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
41
41
  sortBy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
42
- name: "name";
43
42
  size: "size";
43
+ name: "name";
44
44
  path: "path";
45
45
  modified: "modified";
46
46
  }>>>;
@@ -208,18 +208,18 @@ export declare const ReadMultipleFilesOutputSchema: z.ZodObject<{
208
208
  }>>;
209
209
  error: z.ZodOptional<z.ZodObject<{
210
210
  code: z.ZodEnum<{
211
- readonly E_ACCESS_DENIED: "E_ACCESS_DENIED";
212
- readonly E_NOT_FOUND: "E_NOT_FOUND";
213
- readonly E_NOT_FILE: "E_NOT_FILE";
214
- readonly E_NOT_DIRECTORY: "E_NOT_DIRECTORY";
215
- readonly E_TOO_LARGE: "E_TOO_LARGE";
216
- readonly E_TIMEOUT: "E_TIMEOUT";
217
- readonly E_CANCELLED: "E_CANCELLED";
218
- readonly E_INVALID_PATTERN: "E_INVALID_PATTERN";
219
- readonly E_INVALID_INPUT: "E_INVALID_INPUT";
220
- readonly E_PERMISSION_DENIED: "E_PERMISSION_DENIED";
221
- readonly E_SYMLINK_NOT_ALLOWED: "E_SYMLINK_NOT_ALLOWED";
222
- readonly E_UNKNOWN: "E_UNKNOWN";
211
+ readonly ACCESS_DENIED: "ACCESS_DENIED";
212
+ readonly NOT_FOUND: "NOT_FOUND";
213
+ readonly NOT_FILE: "NOT_FILE";
214
+ readonly NOT_DIRECTORY: "NOT_DIRECTORY";
215
+ readonly TOO_LARGE: "TOO_LARGE";
216
+ readonly TIMEOUT: "TIMEOUT";
217
+ readonly CANCELLED: "CANCELLED";
218
+ readonly INVALID_PATTERN: "INVALID_PATTERN";
219
+ readonly INVALID_INPUT: "INVALID_INPUT";
220
+ readonly PERMISSION_DENIED: "PERMISSION_DENIED";
221
+ readonly SYMLINK_NOT_ALLOWED: "SYMLINK_NOT_ALLOWED";
222
+ readonly UNKNOWN: "UNKNOWN";
223
223
  }>;
224
224
  message: z.ZodString;
225
225
  path: z.ZodOptional<z.ZodString>;
@@ -279,18 +279,18 @@ export declare const GetMultipleFileInfoOutputSchema: z.ZodObject<{
279
279
  }, z.core.$strict>>;
280
280
  error: z.ZodOptional<z.ZodObject<{
281
281
  code: z.ZodEnum<{
282
- readonly E_ACCESS_DENIED: "E_ACCESS_DENIED";
283
- readonly E_NOT_FOUND: "E_NOT_FOUND";
284
- readonly E_NOT_FILE: "E_NOT_FILE";
285
- readonly E_NOT_DIRECTORY: "E_NOT_DIRECTORY";
286
- readonly E_TOO_LARGE: "E_TOO_LARGE";
287
- readonly E_TIMEOUT: "E_TIMEOUT";
288
- readonly E_CANCELLED: "E_CANCELLED";
289
- readonly E_INVALID_PATTERN: "E_INVALID_PATTERN";
290
- readonly E_INVALID_INPUT: "E_INVALID_INPUT";
291
- readonly E_PERMISSION_DENIED: "E_PERMISSION_DENIED";
292
- readonly E_SYMLINK_NOT_ALLOWED: "E_SYMLINK_NOT_ALLOWED";
293
- readonly E_UNKNOWN: "E_UNKNOWN";
282
+ readonly ACCESS_DENIED: "ACCESS_DENIED";
283
+ readonly NOT_FOUND: "NOT_FOUND";
284
+ readonly NOT_FILE: "NOT_FILE";
285
+ readonly NOT_DIRECTORY: "NOT_DIRECTORY";
286
+ readonly TOO_LARGE: "TOO_LARGE";
287
+ readonly TIMEOUT: "TIMEOUT";
288
+ readonly CANCELLED: "CANCELLED";
289
+ readonly INVALID_PATTERN: "INVALID_PATTERN";
290
+ readonly INVALID_INPUT: "INVALID_INPUT";
291
+ readonly PERMISSION_DENIED: "PERMISSION_DENIED";
292
+ readonly SYMLINK_NOT_ALLOWED: "SYMLINK_NOT_ALLOWED";
293
+ readonly UNKNOWN: "UNKNOWN";
294
294
  }>;
295
295
  message: z.ZodString;
296
296
  path: z.ZodOptional<z.ZodString>;
@@ -354,18 +354,18 @@ export declare const MoveFileOutputSchema: z.ZodObject<{
354
354
  source: z.ZodString;
355
355
  error: z.ZodObject<{
356
356
  code: z.ZodEnum<{
357
- readonly E_ACCESS_DENIED: "E_ACCESS_DENIED";
358
- readonly E_NOT_FOUND: "E_NOT_FOUND";
359
- readonly E_NOT_FILE: "E_NOT_FILE";
360
- readonly E_NOT_DIRECTORY: "E_NOT_DIRECTORY";
361
- readonly E_TOO_LARGE: "E_TOO_LARGE";
362
- readonly E_TIMEOUT: "E_TIMEOUT";
363
- readonly E_CANCELLED: "E_CANCELLED";
364
- readonly E_INVALID_PATTERN: "E_INVALID_PATTERN";
365
- readonly E_INVALID_INPUT: "E_INVALID_INPUT";
366
- readonly E_PERMISSION_DENIED: "E_PERMISSION_DENIED";
367
- readonly E_SYMLINK_NOT_ALLOWED: "E_SYMLINK_NOT_ALLOWED";
368
- readonly E_UNKNOWN: "E_UNKNOWN";
357
+ readonly ACCESS_DENIED: "ACCESS_DENIED";
358
+ readonly NOT_FOUND: "NOT_FOUND";
359
+ readonly NOT_FILE: "NOT_FILE";
360
+ readonly NOT_DIRECTORY: "NOT_DIRECTORY";
361
+ readonly TOO_LARGE: "TOO_LARGE";
362
+ readonly TIMEOUT: "TIMEOUT";
363
+ readonly CANCELLED: "CANCELLED";
364
+ readonly INVALID_PATTERN: "INVALID_PATTERN";
365
+ readonly INVALID_INPUT: "INVALID_INPUT";
366
+ readonly PERMISSION_DENIED: "PERMISSION_DENIED";
367
+ readonly SYMLINK_NOT_ALLOWED: "SYMLINK_NOT_ALLOWED";
368
+ readonly UNKNOWN: "UNKNOWN";
369
369
  }>;
370
370
  message: z.ZodString;
371
371
  path: z.ZodOptional<z.ZodString>;
@@ -431,18 +431,18 @@ export declare const ApplyPatchOutputSchema: z.ZodObject<{
431
431
  linesRemoved: z.ZodOptional<z.ZodInt>;
432
432
  error: z.ZodOptional<z.ZodObject<{
433
433
  code: z.ZodEnum<{
434
- readonly E_ACCESS_DENIED: "E_ACCESS_DENIED";
435
- readonly E_NOT_FOUND: "E_NOT_FOUND";
436
- readonly E_NOT_FILE: "E_NOT_FILE";
437
- readonly E_NOT_DIRECTORY: "E_NOT_DIRECTORY";
438
- readonly E_TOO_LARGE: "E_TOO_LARGE";
439
- readonly E_TIMEOUT: "E_TIMEOUT";
440
- readonly E_CANCELLED: "E_CANCELLED";
441
- readonly E_INVALID_PATTERN: "E_INVALID_PATTERN";
442
- readonly E_INVALID_INPUT: "E_INVALID_INPUT";
443
- readonly E_PERMISSION_DENIED: "E_PERMISSION_DENIED";
444
- readonly E_SYMLINK_NOT_ALLOWED: "E_SYMLINK_NOT_ALLOWED";
445
- readonly E_UNKNOWN: "E_UNKNOWN";
434
+ readonly ACCESS_DENIED: "ACCESS_DENIED";
435
+ readonly NOT_FOUND: "NOT_FOUND";
436
+ readonly NOT_FILE: "NOT_FILE";
437
+ readonly NOT_DIRECTORY: "NOT_DIRECTORY";
438
+ readonly TOO_LARGE: "TOO_LARGE";
439
+ readonly TIMEOUT: "TIMEOUT";
440
+ readonly CANCELLED: "CANCELLED";
441
+ readonly INVALID_PATTERN: "INVALID_PATTERN";
442
+ readonly INVALID_INPUT: "INVALID_INPUT";
443
+ readonly PERMISSION_DENIED: "PERMISSION_DENIED";
444
+ readonly SYMLINK_NOT_ALLOWED: "SYMLINK_NOT_ALLOWED";
445
+ readonly UNKNOWN: "UNKNOWN";
446
446
  }>;
447
447
  message: z.ZodString;
448
448
  path: z.ZodOptional<z.ZodString>;
@@ -458,9 +458,9 @@ export declare const SearchAndReplaceInputSchema: z.ZodObject<{
458
458
  isRegex: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
459
459
  caseSensitive: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
460
460
  dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
461
- includeHidden: z.ZodOptional<z.ZodBoolean>;
462
- includeIgnored: z.ZodOptional<z.ZodBoolean>;
463
- returnDiff: z.ZodOptional<z.ZodBoolean>;
461
+ includeHidden: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
462
+ includeIgnored: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
463
+ returnDiff: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
464
464
  maxFiles: z.ZodOptional<z.ZodInt>;
465
465
  }, z.core.$strict>;
466
466
  export declare const SearchAndReplaceOutputSchema: z.ZodObject<{
@@ -473,18 +473,18 @@ export declare const SearchAndReplaceOutputSchema: z.ZodObject<{
473
473
  path: z.ZodString;
474
474
  error: z.ZodObject<{
475
475
  code: z.ZodEnum<{
476
- readonly E_ACCESS_DENIED: "E_ACCESS_DENIED";
477
- readonly E_NOT_FOUND: "E_NOT_FOUND";
478
- readonly E_NOT_FILE: "E_NOT_FILE";
479
- readonly E_NOT_DIRECTORY: "E_NOT_DIRECTORY";
480
- readonly E_TOO_LARGE: "E_TOO_LARGE";
481
- readonly E_TIMEOUT: "E_TIMEOUT";
482
- readonly E_CANCELLED: "E_CANCELLED";
483
- readonly E_INVALID_PATTERN: "E_INVALID_PATTERN";
484
- readonly E_INVALID_INPUT: "E_INVALID_INPUT";
485
- readonly E_PERMISSION_DENIED: "E_PERMISSION_DENIED";
486
- readonly E_SYMLINK_NOT_ALLOWED: "E_SYMLINK_NOT_ALLOWED";
487
- readonly E_UNKNOWN: "E_UNKNOWN";
476
+ readonly ACCESS_DENIED: "ACCESS_DENIED";
477
+ readonly NOT_FOUND: "NOT_FOUND";
478
+ readonly NOT_FILE: "NOT_FILE";
479
+ readonly NOT_DIRECTORY: "NOT_DIRECTORY";
480
+ readonly TOO_LARGE: "TOO_LARGE";
481
+ readonly TIMEOUT: "TIMEOUT";
482
+ readonly CANCELLED: "CANCELLED";
483
+ readonly INVALID_PATTERN: "INVALID_PATTERN";
484
+ readonly INVALID_INPUT: "INVALID_INPUT";
485
+ readonly PERMISSION_DENIED: "PERMISSION_DENIED";
486
+ readonly SYMLINK_NOT_ALLOWED: "SYMLINK_NOT_ALLOWED";
487
+ readonly UNKNOWN: "UNKNOWN";
488
488
  }>;
489
489
  message: z.ZodString;
490
490
  path: z.ZodOptional<z.ZodString>;
package/dist/schemas.js CHANGED
@@ -20,6 +20,13 @@ const PathSchemaBase = z
20
20
  .max(MAX_PATH_LENGTH, `Path too long (max ${MAX_PATH_LENGTH} chars)`);
21
21
  const OptionalPathSchema = PathSchemaBase.optional();
22
22
  const RequiredPathSchema = PathSchemaBase.min(1, 'Path required');
23
+ const SafeGlobPatternSchema = z
24
+ .string()
25
+ .min(1, 'Pattern required')
26
+ .max(1000, 'Max 1000 chars')
27
+ .refine((val) => isSafeGlobPattern(val), {
28
+ error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
29
+ });
23
30
  const FileTypeSchema = z.enum(['file', 'directory', 'symlink', 'other']);
24
31
  const ListDirectorySortSchema = z.enum(['name', 'size', 'modified', 'type']);
25
32
  const SearchFilesSortSchema = z.enum(['name', 'size', 'modified', 'path']);
@@ -37,7 +44,7 @@ const TreeEntrySchema = z.lazy(() => z.strictObject({
37
44
  children: z.array(TreeEntrySchema).optional().describe('Children'),
38
45
  }));
39
46
  const ErrorSchema = z.strictObject({
40
- code: z.enum(ErrorCode).describe('Error code (e.g. E_NOT_FOUND)'),
47
+ code: z.enum(ErrorCode).describe('Error code (e.g. NOT_FOUND)'),
41
48
  message: z.string().describe('Human-readable message'),
42
49
  path: z.string().optional().describe('Relevant path'),
43
50
  suggestion: z.string().optional().describe('Fix suggestion'),
@@ -126,15 +133,7 @@ export const ListDirectoryInputSchema = z.strictObject({
126
133
  sortBy: ListDirectorySortSchema.optional()
127
134
  .default('name')
128
135
  .describe('Sort field (name, size, modified, type)'),
129
- pattern: z
130
- .string()
131
- .min(1, 'Pattern required')
132
- .max(1000, 'Max 1000 chars')
133
- .refine((val) => isSafeGlobPattern(val), {
134
- error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
135
- })
136
- .optional()
137
- .describe('Optional glob pattern filter (e.g. "**/*.ts")'),
136
+ pattern: SafeGlobPatternSchema.optional().describe('Optional glob pattern filter (e.g. "**/*.ts")'),
138
137
  includeSymlinkTargets: defaultFalseBoolean('Resolve and include symlink targets in results'),
139
138
  cursor: z
140
139
  .string()
@@ -146,14 +145,7 @@ export const ListAllowedDirectoriesInputSchema = z
146
145
  .describe('No input parameters.');
147
146
  export const SearchFilesInputSchema = z.strictObject({
148
147
  path: OptionalPathSchema.describe(DESC_PATH_ROOT),
149
- pattern: z
150
- .string()
151
- .min(1, 'Pattern required')
152
- .max(1000, 'Max 1000 chars')
153
- .refine((val) => isSafeGlobPattern(val), {
154
- error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
155
- })
156
- .describe('Glob pattern (e.g. "**/*.ts", "src/*.js")'),
148
+ pattern: SafeGlobPatternSchema.describe('Glob pattern (e.g. "**/*.ts", "src/*.js")'),
157
149
  maxResults: z
158
150
  .int({ error: 'Must be integer' })
159
151
  .min(1, 'Min: 1')
@@ -221,11 +213,7 @@ export const SearchContentInputSchema = z.strictObject({
221
213
  .optional()
222
214
  .default(DEFAULT_SEARCH_CONTENT_RESULTS)
223
215
  .describe(`Maximum match rows to return. Default: ${DEFAULT_SEARCH_CONTENT_RESULTS}`),
224
- filePattern: z
225
- .string()
226
- .min(1, 'Pattern required')
227
- .max(1000, 'Max 1000 chars')
228
- .optional()
216
+ filePattern: SafeGlobPatternSchema.optional()
229
217
  .default('**/*')
230
218
  .describe('Glob for candidate files (e.g. "**/*.ts")'),
231
219
  includeHidden: defaultFalseBoolean('Include hidden items (starting with .)'),
@@ -449,6 +437,8 @@ export const EditFileInputSchema = z.strictObject({
449
437
  .array(z.strictObject({
450
438
  oldText: z
451
439
  .string()
440
+ .min(1, 'oldText required')
441
+ .max(102400, 'Max 100KB')
452
442
  .describe('Exact literal string to replace (character-for-character). Include 3–5 lines of context for unique targeting.'),
453
443
  newText: z
454
444
  .string()
@@ -621,15 +611,8 @@ export const ApplyPatchOutputSchema = z.strictObject({
621
611
  });
622
612
  export const SearchAndReplaceInputSchema = z.strictObject({
623
613
  path: OptionalPathSchema.describe(DESC_PATH_ROOT),
624
- filePattern: z
625
- .string()
626
- .min(1, 'Pattern required')
627
- .max(1000, 'Max 1000 chars')
628
- .optional()
614
+ filePattern: SafeGlobPatternSchema.optional()
629
615
  .default('**/*')
630
- .refine((val) => isSafeGlobPattern(val), {
631
- error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
632
- })
633
616
  .describe('Glob to filter files. Default: **/*'),
634
617
  searchPattern: z
635
618
  .string()
@@ -644,18 +627,9 @@ export const SearchAndReplaceInputSchema = z.strictObject({
644
627
  .default(true)
645
628
  .describe('Case-sensitive matching. Default: true.'),
646
629
  dryRun: defaultFalseBoolean('Preview matches without writing. Check changedFiles and matches in the response before committing.'),
647
- includeHidden: z
648
- .boolean()
649
- .optional()
650
- .describe('Include hidden files/directories (starting with .). Default: false.'),
651
- includeIgnored: z
652
- .boolean()
653
- .optional()
654
- .describe('Include .gitignore-ignored files (node_modules, dist). Default: false.'),
655
- returnDiff: z
656
- .boolean()
657
- .optional()
658
- .describe('Return unified diff of changes even if dryRun is false. Default: false.'),
630
+ includeHidden: defaultFalseBoolean('Include hidden files/directories (starting with .). Default: false.'),
631
+ includeIgnored: defaultFalseBoolean('Include .gitignore-ignored files (node_modules, dist). Default: false.'),
632
+ returnDiff: defaultFalseBoolean('Return unified diff of changes even if dryRun is false. Default: false.'),
659
633
  maxFiles: z
660
634
  .int({ error: 'Must be integer' })
661
635
  .min(1, 'Min: 1')
@@ -687,7 +661,10 @@ export const SearchAndReplaceOutputSchema = z.strictObject({
687
661
  .boolean()
688
662
  .optional()
689
663
  .describe('Changed file list truncated'),
690
- diff: z.string().optional().describe('Unified diff of changes (dryRun only)'),
664
+ diff: z
665
+ .string()
666
+ .optional()
667
+ .describe('Unified diff of changes when `dryRun` or `returnDiff` is enabled'),
691
668
  diffTruncated: z
692
669
  .boolean()
693
670
  .optional()
@@ -1,14 +1,15 @@
1
- import * as http from 'node:http';
2
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import type { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
4
- export interface ServerOptions {
5
- allowCwd?: boolean;
6
- cliAllowedDirs?: string[];
7
- }
8
- export interface LoggingState {
9
- minimumLevel: LoggingLevel;
10
- }
11
- export declare function logToMcp(server: McpServer | undefined, level: LoggingLevel, data: string, minLevel?: LoggingLevel): void;
2
+ import { type Server } from 'node:http';
3
+ import { type LoggingState } from '../lib/logger.js';
4
+ import { type ServerOptions } from './roots-manager.js';
5
+ export declare const activeServers: Map<string, {
6
+ server: McpServer;
7
+ loggingState: LoggingState;
8
+ }>;
9
+ export declare let stdioServer: {
10
+ server: McpServer;
11
+ loggingState: LoggingState;
12
+ } | undefined;
12
13
  export declare function createServer(options?: ServerOptions): Promise<McpServer>;
13
14
  export declare function startServer(server: McpServer): Promise<void>;
14
- export declare function startHttpServer(port: number, options: ServerOptions): Promise<http.Server>;
15
+ export declare function startHttpServer(port: number, options: ServerOptions): Promise<Server>;