@j0hanz/filesystem-mcp 1.2.4 → 1.3.1
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.
- package/README.md +8 -0
- package/dist/completions.d.ts +1 -1
- package/dist/completions.js +36 -1
- package/dist/lib/observability.d.ts +6 -0
- package/dist/lib/observability.js +1 -1
- package/dist/lib/resource-store.js +53 -0
- package/dist/prompts.js +34 -14
- package/dist/resources/generated-instructions.d.ts +1 -0
- package/dist/resources/generated-instructions.js +100 -0
- package/dist/resources.d.ts +1 -0
- package/dist/resources.js +36 -1
- package/dist/schemas.d.ts +6 -0
- package/dist/schemas.js +24 -0
- package/dist/server/bootstrap.js +45 -22
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tools/apply-patch.d.ts +2 -1
- package/dist/tools/apply-patch.js +7 -5
- package/dist/tools/calculate-hash.d.ts +2 -1
- package/dist/tools/calculate-hash.js +9 -5
- package/dist/tools/contract.d.ts +41 -0
- package/dist/tools/contract.js +1 -0
- package/dist/tools/create-directory.d.ts +2 -1
- package/dist/tools/create-directory.js +6 -5
- package/dist/tools/delete-file.d.ts +2 -1
- package/dist/tools/delete-file.js +9 -5
- package/dist/tools/diff-files.d.ts +2 -1
- package/dist/tools/diff-files.js +7 -4
- package/dist/tools/edit-file.d.ts +2 -1
- package/dist/tools/edit-file.js +19 -6
- package/dist/tools/list-directory.d.ts +2 -1
- package/dist/tools/list-directory.js +36 -7
- package/dist/tools/move-file.d.ts +2 -1
- package/dist/tools/move-file.js +7 -5
- package/dist/tools/read-multiple.d.ts +2 -1
- package/dist/tools/read-multiple.js +10 -5
- package/dist/tools/read.d.ts +2 -1
- package/dist/tools/read.js +9 -5
- package/dist/tools/replace-in-files.d.ts +2 -1
- package/dist/tools/replace-in-files.js +14 -7
- package/dist/tools/roots.d.ts +2 -1
- package/dist/tools/roots.js +7 -4
- package/dist/tools/search-content.d.ts +2 -1
- package/dist/tools/search-content.js +14 -6
- package/dist/tools/search-files.d.ts +2 -1
- package/dist/tools/search-files.js +39 -7
- package/dist/tools/shared.d.ts +2 -2
- package/dist/tools/shared.js +16 -1
- package/dist/tools/stat-many.d.ts +2 -1
- package/dist/tools/stat-many.js +7 -5
- package/dist/tools/stat.d.ts +2 -1
- package/dist/tools/stat.js +7 -4
- package/dist/tools/task-support.d.ts +2 -0
- package/dist/tools/task-support.js +61 -9
- package/dist/tools/tree.d.ts +2 -1
- package/dist/tools/tree.js +7 -5
- package/dist/tools/write-file.d.ts +2 -1
- package/dist/tools/write-file.js +12 -5
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +39 -18
- package/package.json +1 -2
- package/dist/instructions.md +0 -200
package/dist/server.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createServer, startHttpServer, startServer } from './server/bootstrap.js';
|
|
1
|
+
export { createServer, startHttpServer, startServer, } from './server/bootstrap.js';
|
|
2
2
|
export type { ServerOptions } from './server/types.js';
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createServer, startHttpServer, startServer } from './server/bootstrap.js';
|
|
1
|
+
export { createServer, startHttpServer, startServer, } from './server/bootstrap.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const APPLY_PATCH_TOOL: ToolContract;
|
|
3
4
|
export declare function registerApplyPatchTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -8,7 +8,8 @@ import { validateExistingPath } from '../lib/path-validation.js';
|
|
|
8
8
|
import { ApplyPatchInputSchema, ApplyPatchOutputSchema } from '../schemas.js';
|
|
9
9
|
import { buildToolErrorResponse, buildToolResponse, DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS, executeToolWithDiagnostics, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
10
10
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
11
|
-
const APPLY_PATCH_TOOL = {
|
|
11
|
+
export const APPLY_PATCH_TOOL = {
|
|
12
|
+
name: 'apply_patch',
|
|
12
13
|
title: 'Apply Patch',
|
|
13
14
|
description: 'Apply a unified diff patch to a file. ' +
|
|
14
15
|
'Generate the patch with `diff_files`, then validate with `dryRun: true` before writing. ' +
|
|
@@ -16,6 +17,7 @@ const APPLY_PATCH_TOOL = {
|
|
|
16
17
|
inputSchema: ApplyPatchInputSchema,
|
|
17
18
|
outputSchema: ApplyPatchOutputSchema,
|
|
18
19
|
annotations: DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
|
|
20
|
+
gotchas: ['Patch must include valid hunk headers; use `dryRun=true` first.'],
|
|
19
21
|
};
|
|
20
22
|
function assertPatchTargetSizeWithinLimit(filePath, size, maxFileSize) {
|
|
21
23
|
if (size <= maxFileSize)
|
|
@@ -69,8 +71,7 @@ export function registerApplyPatchTool(server, options = {}) {
|
|
|
69
71
|
run: (signal) => handleApplyPatch(args, signal),
|
|
70
72
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path),
|
|
71
73
|
});
|
|
72
|
-
const
|
|
73
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
74
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
74
75
|
guard: options.isInitialized,
|
|
75
76
|
progressMessage: (args) => {
|
|
76
77
|
const name = path.basename(args.path);
|
|
@@ -90,7 +91,8 @@ export function registerApplyPatchTool(server, options = {}) {
|
|
|
90
91
|
return `🛠 apply_patch: ${name} • applied`;
|
|
91
92
|
},
|
|
92
93
|
});
|
|
93
|
-
|
|
94
|
+
const validatedHandler = withValidatedArgs(ApplyPatchInputSchema, wrappedHandler);
|
|
95
|
+
if (registerToolTaskIfAvailable(server, 'apply_patch', APPLY_PATCH_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
94
96
|
return;
|
|
95
|
-
server.registerTool('apply_patch', withDefaultIcons({ ...APPLY_PATCH_TOOL }, options.iconInfo),
|
|
97
|
+
server.registerTool('apply_patch', withDefaultIcons({ ...APPLY_PATCH_TOOL }, options.iconInfo), validatedHandler);
|
|
96
98
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const CALCULATE_HASH_TOOL: ToolContract;
|
|
3
4
|
export declare function registerCalculateHashTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -12,12 +12,16 @@ import { CalculateHashInputSchema, CalculateHashOutputSchema, } from '../schemas
|
|
|
12
12
|
import { buildToolErrorResponse, buildToolResponse, createProgressReporter, executeToolWithDiagnostics, notifyProgress, READ_ONLY_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
13
13
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
14
14
|
const WINDOWS_PATH_SEPARATOR = /\\/gu;
|
|
15
|
-
const CALCULATE_HASH_TOOL = {
|
|
15
|
+
export const CALCULATE_HASH_TOOL = {
|
|
16
|
+
name: 'calculate_hash',
|
|
16
17
|
title: 'Calculate Hash',
|
|
17
18
|
description: 'Calculate SHA-256 hash of a file or directory.',
|
|
18
19
|
inputSchema: CalculateHashInputSchema,
|
|
19
20
|
outputSchema: CalculateHashOutputSchema,
|
|
20
21
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
22
|
+
nuances: [
|
|
23
|
+
'Directory hashing respects root `.gitignore` and sorts paths for stable output.',
|
|
24
|
+
],
|
|
21
25
|
};
|
|
22
26
|
async function hashFile(filePath, encoding, signal) {
|
|
23
27
|
assertNotAborted(signal);
|
|
@@ -204,11 +208,11 @@ export function registerCalculateHashTool(server, options = {}) {
|
|
|
204
208
|
},
|
|
205
209
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path),
|
|
206
210
|
});
|
|
207
|
-
const
|
|
208
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
211
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
209
212
|
guard: options.isInitialized,
|
|
210
213
|
});
|
|
211
|
-
|
|
214
|
+
const validatedHandler = withValidatedArgs(CalculateHashInputSchema, wrappedHandler);
|
|
215
|
+
if (registerToolTaskIfAvailable(server, 'calculate_hash', CALCULATE_HASH_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
212
216
|
return;
|
|
213
|
-
server.registerTool('calculate_hash', withDefaultIcons({ ...CALCULATE_HASH_TOOL }, options.iconInfo),
|
|
217
|
+
server.registerTool('calculate_hash', withDefaultIcons({ ...CALCULATE_HASH_TOOL }, options.iconInfo), validatedHandler);
|
|
214
218
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ZodType } from 'zod';
|
|
2
|
+
export interface ToolContract {
|
|
3
|
+
/**
|
|
4
|
+
* The unique name of the tool (e.g., "read", "grep").
|
|
5
|
+
* This name is used in registration and client calls.
|
|
6
|
+
*/
|
|
7
|
+
name: string;
|
|
8
|
+
/**
|
|
9
|
+
* A short human-readable title for documentation (e.g., "Read File").
|
|
10
|
+
*/
|
|
11
|
+
title: string;
|
|
12
|
+
/**
|
|
13
|
+
* A detailed description of what the tool does.
|
|
14
|
+
*/
|
|
15
|
+
description: string;
|
|
16
|
+
/**
|
|
17
|
+
* Zod schema for the tool's input arguments.
|
|
18
|
+
*/
|
|
19
|
+
inputSchema: ZodType;
|
|
20
|
+
/**
|
|
21
|
+
* Zod schema for the tool's output result (optional).
|
|
22
|
+
*/
|
|
23
|
+
outputSchema?: ZodType;
|
|
24
|
+
/**
|
|
25
|
+
* Optional annotations for tool behavior hints.
|
|
26
|
+
*/
|
|
27
|
+
annotations?: {
|
|
28
|
+
readOnlyHint?: boolean;
|
|
29
|
+
idempotentHint?: boolean;
|
|
30
|
+
destructiveHint?: boolean;
|
|
31
|
+
openWorldHint?: boolean;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Specific usage nuances or edge cases for documentation.
|
|
35
|
+
*/
|
|
36
|
+
nuances?: string[];
|
|
37
|
+
/**
|
|
38
|
+
* Common pitfalls or warnings for documentation.
|
|
39
|
+
*/
|
|
40
|
+
gotchas?: string[];
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const CREATE_DIRECTORY_TOOL: ToolContract;
|
|
3
4
|
export declare function registerCreateDirectoryTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -6,7 +6,8 @@ import { validatePathForWrite } from '../lib/path-validation.js';
|
|
|
6
6
|
import { CreateDirectoryInputSchema, CreateDirectoryOutputSchema, } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, IDEMPOTENT_WRITE_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
8
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
9
|
-
const CREATE_DIRECTORY_TOOL = {
|
|
9
|
+
export const CREATE_DIRECTORY_TOOL = {
|
|
10
|
+
name: 'mkdir',
|
|
10
11
|
title: 'Create Directory',
|
|
11
12
|
description: 'Create a new directory at the specified path (recursive)',
|
|
12
13
|
inputSchema: CreateDirectoryInputSchema,
|
|
@@ -30,8 +31,7 @@ export function registerCreateDirectoryTool(server, options = {}) {
|
|
|
30
31
|
run: (signal) => handleCreateDirectory(args, signal),
|
|
31
32
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path),
|
|
32
33
|
});
|
|
33
|
-
const
|
|
34
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
34
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
35
35
|
guard: options.isInitialized,
|
|
36
36
|
progressMessage: (args) => {
|
|
37
37
|
const name = path.basename(args.path) || args.path;
|
|
@@ -44,7 +44,8 @@ export function registerCreateDirectoryTool(server, options = {}) {
|
|
|
44
44
|
return `🛠 mkdir: ${name} • created`;
|
|
45
45
|
},
|
|
46
46
|
});
|
|
47
|
-
|
|
47
|
+
const validatedHandler = withValidatedArgs(CreateDirectoryInputSchema, wrappedHandler);
|
|
48
|
+
if (registerToolTaskIfAvailable(server, 'mkdir', CREATE_DIRECTORY_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
48
49
|
return;
|
|
49
|
-
server.registerTool('mkdir', withDefaultIcons({ ...CREATE_DIRECTORY_TOOL }, options.iconInfo),
|
|
50
|
+
server.registerTool('mkdir', withDefaultIcons({ ...CREATE_DIRECTORY_TOOL }, options.iconInfo), validatedHandler);
|
|
50
51
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const DELETE_FILE_TOOL: ToolContract;
|
|
3
4
|
export declare function registerDeleteFileTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -6,12 +6,16 @@ import { validatePathForWrite } from '../lib/path-validation.js';
|
|
|
6
6
|
import { DeleteFileInputSchema, DeleteFileOutputSchema } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS, executeToolWithDiagnostics, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
8
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
9
|
-
const DELETE_FILE_TOOL = {
|
|
9
|
+
export const DELETE_FILE_TOOL = {
|
|
10
|
+
name: 'rm',
|
|
10
11
|
title: 'Delete File',
|
|
11
12
|
description: 'Delete a file or directory.',
|
|
12
13
|
inputSchema: DeleteFileInputSchema,
|
|
13
14
|
outputSchema: DeleteFileOutputSchema,
|
|
14
15
|
annotations: DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
|
|
16
|
+
gotchas: [
|
|
17
|
+
'Non-empty directory delete requires `recursive=true`; else returns actionable input error.',
|
|
18
|
+
],
|
|
15
19
|
};
|
|
16
20
|
async function handleDeleteFile(args, signal) {
|
|
17
21
|
const validPath = await validatePathForWrite(args.path, signal);
|
|
@@ -74,8 +78,7 @@ export function registerDeleteFileTool(server, options = {}) {
|
|
|
74
78
|
return buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path);
|
|
75
79
|
},
|
|
76
80
|
});
|
|
77
|
-
const
|
|
78
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
81
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
79
82
|
guard: options.isInitialized,
|
|
80
83
|
progressMessage: (args) => `🛠 rm: ${path.basename(args.path)}`,
|
|
81
84
|
completionMessage: (args, result) => {
|
|
@@ -85,7 +88,8 @@ export function registerDeleteFileTool(server, options = {}) {
|
|
|
85
88
|
return `🛠 rm: ${name} • deleted`;
|
|
86
89
|
},
|
|
87
90
|
});
|
|
88
|
-
|
|
91
|
+
const validatedHandler = withValidatedArgs(DeleteFileInputSchema, wrappedHandler);
|
|
92
|
+
if (registerToolTaskIfAvailable(server, 'rm', DELETE_FILE_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
89
93
|
return;
|
|
90
|
-
server.registerTool('rm', withDefaultIcons({ ...DELETE_FILE_TOOL }, options.iconInfo),
|
|
94
|
+
server.registerTool('rm', withDefaultIcons({ ...DELETE_FILE_TOOL }, options.iconInfo), validatedHandler);
|
|
91
95
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const DIFF_FILES_TOOL: ToolContract;
|
|
3
4
|
export declare function registerDiffFilesTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
package/dist/tools/diff-files.js
CHANGED
|
@@ -7,7 +7,8 @@ import { withAbort } from '../lib/fs-helpers.js';
|
|
|
7
7
|
import { validateExistingPath } from '../lib/path-validation.js';
|
|
8
8
|
import { DiffFilesInputSchema, DiffFilesOutputSchema } from '../schemas.js';
|
|
9
9
|
import { buildResourceLink, buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, maybeExternalizeTextContent, READ_ONLY_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
10
|
-
const DIFF_FILES_TOOL = {
|
|
10
|
+
export const DIFF_FILES_TOOL = {
|
|
11
|
+
name: 'diff_files',
|
|
11
12
|
title: 'Diff Files',
|
|
12
13
|
description: 'Generate a unified diff between two files. ' +
|
|
13
14
|
'Output feeds directly into `apply_patch`. ' +
|
|
@@ -15,6 +16,7 @@ const DIFF_FILES_TOOL = {
|
|
|
15
16
|
inputSchema: DiffFilesInputSchema,
|
|
16
17
|
outputSchema: DiffFilesOutputSchema,
|
|
17
18
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
19
|
+
gotchas: ['`isIdentical=true` means no hunks (`@@`) and empty diff.'],
|
|
18
20
|
};
|
|
19
21
|
function assertDiffFileSizeWithinLimit(filePath, size, maxFileSize) {
|
|
20
22
|
if (size <= maxFileSize)
|
|
@@ -80,8 +82,7 @@ export function registerDiffFilesTool(server, options = {}) {
|
|
|
80
82
|
run: (signal) => handleDiffFiles(args, signal, options.resourceStore),
|
|
81
83
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.original),
|
|
82
84
|
});
|
|
83
|
-
const
|
|
84
|
-
server.registerTool('diff_files', withDefaultIcons({ ...DIFF_FILES_TOOL }, options.iconInfo), wrapToolHandler(validatedHandler, {
|
|
85
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
85
86
|
guard: options.isInitialized,
|
|
86
87
|
progressMessage: (args) => {
|
|
87
88
|
const name1 = path.basename(args.original);
|
|
@@ -101,5 +102,7 @@ export function registerDiffFilesTool(server, options = {}) {
|
|
|
101
102
|
const hunks = (sc.diff?.match(/@@/g) ?? []).length;
|
|
102
103
|
return `🕮 diff_files: ${n1} ⟷ ${n2} • ${hunks} hunk${hunks !== 1 ? 's' : ''}`;
|
|
103
104
|
},
|
|
104
|
-
})
|
|
105
|
+
});
|
|
106
|
+
const validatedHandler = withValidatedArgs(DiffFilesInputSchema, wrappedHandler);
|
|
107
|
+
server.registerTool('diff_files', withDefaultIcons({ ...DIFF_FILES_TOOL }, options.iconInfo), validatedHandler);
|
|
105
108
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const EDIT_FILE_TOOL: ToolContract;
|
|
3
4
|
export declare function registerEditFileTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -5,7 +5,8 @@ import { atomicWriteFile } from '../lib/fs-helpers.js';
|
|
|
5
5
|
import { validateExistingPath } from '../lib/path-validation.js';
|
|
6
6
|
import { EditFileInputSchema, EditFileOutputSchema } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS, executeToolWithDiagnostics, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
|
-
const EDIT_FILE_TOOL = {
|
|
8
|
+
export const EDIT_FILE_TOOL = {
|
|
9
|
+
name: 'edit',
|
|
9
10
|
title: 'Edit File',
|
|
10
11
|
description: 'Edit a file by replacing text. Sequentially applies a list of string replacements. ' +
|
|
11
12
|
'Replaces the first occurrence of each `oldText`. ' +
|
|
@@ -14,6 +15,12 @@ const EDIT_FILE_TOOL = {
|
|
|
14
15
|
inputSchema: EditFileInputSchema,
|
|
15
16
|
outputSchema: EditFileOutputSchema,
|
|
16
17
|
annotations: DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
|
|
18
|
+
nuances: [
|
|
19
|
+
'Apply sequential literal replacements (first occurrence per edit).',
|
|
20
|
+
],
|
|
21
|
+
gotchas: [
|
|
22
|
+
'`oldText` must match exactly; unmatched items are reported in `unmatchedEdits`.',
|
|
23
|
+
],
|
|
17
24
|
};
|
|
18
25
|
function applyEdits(content, edits) {
|
|
19
26
|
let newContent = content;
|
|
@@ -65,9 +72,14 @@ async function handleEditFile(args, signal) {
|
|
|
65
72
|
if (appliedEdits > 0) {
|
|
66
73
|
await atomicWriteFile(validPath, newContent, { encoding: 'utf-8', signal });
|
|
67
74
|
}
|
|
75
|
+
const unmatchedNote = unmatchedEdits.length > 0
|
|
76
|
+
? ` — ${unmatchedEdits.length} unmatched: [${unmatchedEdits
|
|
77
|
+
.map((s) => JSON.stringify(s.length > 40 ? `${s.slice(0, 40)}\u2026` : s))
|
|
78
|
+
.join(', ')}]`
|
|
79
|
+
: '';
|
|
68
80
|
const message = appliedEdits === 0
|
|
69
|
-
? `No edits applied to ${args.path}`
|
|
70
|
-
: `Successfully applied ${appliedEdits} edits to ${args.path}`;
|
|
81
|
+
? `No edits applied to ${args.path}${unmatchedNote}`
|
|
82
|
+
: `Successfully applied ${appliedEdits} edits to ${args.path}${unmatchedNote}`;
|
|
71
83
|
return buildToolResponse(message, structured);
|
|
72
84
|
}
|
|
73
85
|
export function registerEditFileTool(server, options = {}) {
|
|
@@ -79,8 +91,7 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
79
91
|
run: (signal) => handleEditFile(args, signal),
|
|
80
92
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path),
|
|
81
93
|
});
|
|
82
|
-
const
|
|
83
|
-
server.registerTool('edit', withDefaultIcons({ ...EDIT_FILE_TOOL }, options.iconInfo), wrapToolHandler(validatedHandler, {
|
|
94
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
84
95
|
guard: options.isInitialized,
|
|
85
96
|
progressMessage: (args) => {
|
|
86
97
|
const name = path.basename(args.path);
|
|
@@ -98,5 +109,7 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
98
109
|
}
|
|
99
110
|
return `🛠 edit: ${name} • [${sc.appliedEdits ?? 0} edits]`;
|
|
100
111
|
},
|
|
101
|
-
})
|
|
112
|
+
});
|
|
113
|
+
const validatedHandler = withValidatedArgs(EditFileInputSchema, wrappedHandler);
|
|
114
|
+
server.registerTool('edit', withDefaultIcons({ ...EDIT_FILE_TOOL }, options.iconInfo), validatedHandler);
|
|
102
115
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const LIST_DIRECTORY_TOOL: ToolContract;
|
|
3
4
|
export declare function registerListDirectoryTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -5,7 +5,8 @@ import { ErrorCode } from '../lib/errors.js';
|
|
|
5
5
|
import { listDirectory } from '../lib/file-operations/list-directory.js';
|
|
6
6
|
import { ListDirectoryInputSchema, ListDirectoryOutputSchema, } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, resolvePathOrRoot, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
|
-
const LIST_DIRECTORY_TOOL = {
|
|
8
|
+
export const LIST_DIRECTORY_TOOL = {
|
|
9
|
+
name: 'ls',
|
|
9
10
|
title: 'List Directory',
|
|
10
11
|
description: 'List the immediate contents of a directory (non-recursive). ' +
|
|
11
12
|
'Returns name, relative path, type (file/directory/symlink), size, and modified date. ' +
|
|
@@ -15,6 +16,7 @@ const LIST_DIRECTORY_TOOL = {
|
|
|
15
16
|
inputSchema: ListDirectoryInputSchema,
|
|
16
17
|
outputSchema: ListDirectoryOutputSchema,
|
|
17
18
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
19
|
+
nuances: ['`pattern` enables filtered recursive traversal up to `maxDepth`.'],
|
|
18
20
|
};
|
|
19
21
|
function buildListTextResult(result) {
|
|
20
22
|
const { entries, summary, path } = result;
|
|
@@ -53,7 +55,7 @@ function buildStructuredListEntry(entry) {
|
|
|
53
55
|
modified: entry.modified?.toISOString(),
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
|
-
function buildStructuredListResult(result) {
|
|
58
|
+
function buildStructuredListResult(result, nextCursor) {
|
|
57
59
|
const { entries, summary, path: resultPath } = result;
|
|
58
60
|
const structuredEntries = [];
|
|
59
61
|
for (const entry of entries) {
|
|
@@ -71,22 +73,48 @@ function buildStructuredListResult(result) {
|
|
|
71
73
|
...(summary.skippedInaccessible
|
|
72
74
|
? { skippedInaccessible: summary.skippedInaccessible }
|
|
73
75
|
: {}),
|
|
76
|
+
...(nextCursor !== undefined ? { nextCursor } : {}),
|
|
74
77
|
};
|
|
75
78
|
}
|
|
79
|
+
function encodeCursor(offset) {
|
|
80
|
+
return Buffer.from(JSON.stringify({ offset })).toString('base64url');
|
|
81
|
+
}
|
|
82
|
+
function decodeCursor(cursor) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf-8'));
|
|
85
|
+
if (typeof parsed === 'object' &&
|
|
86
|
+
parsed !== null &&
|
|
87
|
+
typeof parsed.offset === 'number') {
|
|
88
|
+
const { offset } = parsed;
|
|
89
|
+
return Number.isInteger(offset) && offset >= 0 ? offset : 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// ignore malformed cursor
|
|
94
|
+
}
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
76
97
|
async function handleListDirectory(args, signal) {
|
|
77
98
|
const dirPath = resolvePathOrRoot(args.path);
|
|
99
|
+
const cursorOffset = args.cursor !== undefined ? decodeCursor(args.cursor) : 0;
|
|
100
|
+
const pageSize = args.maxEntries ?? 20_000;
|
|
78
101
|
const options = {
|
|
79
102
|
includeHidden: args.includeHidden,
|
|
80
103
|
excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,
|
|
81
104
|
sortBy: args.sortBy,
|
|
82
105
|
includeSymlinkTargets: args.includeSymlinkTargets,
|
|
83
106
|
...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),
|
|
84
|
-
|
|
107
|
+
maxEntries: cursorOffset + pageSize,
|
|
85
108
|
...(args.pattern !== undefined ? { pattern: args.pattern } : {}),
|
|
86
109
|
...(signal ? { signal } : {}),
|
|
87
110
|
};
|
|
88
111
|
const result = await listDirectory(dirPath, options);
|
|
89
|
-
|
|
112
|
+
const displayEntries = cursorOffset > 0 ? result.entries.slice(cursorOffset) : result.entries;
|
|
113
|
+
const nextCursor = result.summary.truncated && displayEntries.length > 0
|
|
114
|
+
? encodeCursor(cursorOffset + displayEntries.length)
|
|
115
|
+
: undefined;
|
|
116
|
+
const displayResult = { ...result, entries: displayEntries };
|
|
117
|
+
return buildToolResponse(buildListTextResult(displayResult), buildStructuredListResult(displayResult, nextCursor));
|
|
90
118
|
}
|
|
91
119
|
export function registerListDirectoryTool(server, options = {}) {
|
|
92
120
|
const handler = (args, extra) => executeToolWithDiagnostics({
|
|
@@ -96,8 +124,7 @@ export function registerListDirectoryTool(server, options = {}) {
|
|
|
96
124
|
run: (signal) => handleListDirectory(args, signal),
|
|
97
125
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_NOT_DIRECTORY, args.path ?? '.'),
|
|
98
126
|
});
|
|
99
|
-
const
|
|
100
|
-
server.registerTool('ls', withDefaultIcons({ ...LIST_DIRECTORY_TOOL }, options.iconInfo), wrapToolHandler(validatedHandler, {
|
|
127
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
101
128
|
guard: options.isInitialized,
|
|
102
129
|
progressMessage: (args) => {
|
|
103
130
|
if (args.path) {
|
|
@@ -115,5 +142,7 @@ export function registerListDirectoryTool(server, options = {}) {
|
|
|
115
142
|
const count = sc.totalEntries ?? 0;
|
|
116
143
|
return `≣ ls: ${base} • ${count} ${count === 1 ? 'entry' : 'entries'}`;
|
|
117
144
|
},
|
|
118
|
-
})
|
|
145
|
+
});
|
|
146
|
+
const validatedHandler = withValidatedArgs(ListDirectoryInputSchema, wrappedHandler);
|
|
147
|
+
server.registerTool('ls', withDefaultIcons({ ...LIST_DIRECTORY_TOOL }, options.iconInfo), validatedHandler);
|
|
119
148
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const MOVE_FILE_TOOL: ToolContract;
|
|
3
4
|
export declare function registerMoveFileTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
package/dist/tools/move-file.js
CHANGED
|
@@ -6,12 +6,14 @@ import { validateExistingPath, validatePathForWrite, } from '../lib/path-validat
|
|
|
6
6
|
import { MoveFileInputSchema, MoveFileOutputSchema } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS, executeToolWithDiagnostics, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
8
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
9
|
-
const MOVE_FILE_TOOL = {
|
|
9
|
+
export const MOVE_FILE_TOOL = {
|
|
10
|
+
name: 'mv',
|
|
10
11
|
title: 'Move File',
|
|
11
12
|
description: 'Move or rename a file or directory.',
|
|
12
13
|
inputSchema: MoveFileInputSchema,
|
|
13
14
|
outputSchema: MoveFileOutputSchema,
|
|
14
15
|
annotations: DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
|
|
16
|
+
nuances: ['Cross-device moves fall back to copy+delete.'],
|
|
15
17
|
};
|
|
16
18
|
async function handleMoveFile(args, signal) {
|
|
17
19
|
const validSource = await validateExistingPath(args.source, signal);
|
|
@@ -46,8 +48,7 @@ export function registerMoveFileTool(server, options = {}) {
|
|
|
46
48
|
run: (signal) => handleMoveFile(args, signal),
|
|
47
49
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.source),
|
|
48
50
|
});
|
|
49
|
-
const
|
|
50
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
51
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
51
52
|
guard: options.isInitialized,
|
|
52
53
|
progressMessage: (args) => `🛠 mv: ${path.basename(args.source)} → ${path.basename(args.destination)}`,
|
|
53
54
|
completionMessage: (args, result) => {
|
|
@@ -58,7 +59,8 @@ export function registerMoveFileTool(server, options = {}) {
|
|
|
58
59
|
return `🛠 mv: ${src} → ${dst} • moved`;
|
|
59
60
|
},
|
|
60
61
|
});
|
|
61
|
-
|
|
62
|
+
const validatedHandler = withValidatedArgs(MoveFileInputSchema, wrappedHandler);
|
|
63
|
+
if (registerToolTaskIfAvailable(server, 'mv', MOVE_FILE_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
62
64
|
return;
|
|
63
|
-
server.registerTool('mv', withDefaultIcons({ ...MOVE_FILE_TOOL }, options.iconInfo),
|
|
65
|
+
server.registerTool('mv', withDefaultIcons({ ...MOVE_FILE_TOOL }, options.iconInfo), validatedHandler);
|
|
64
66
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const READ_MULTIPLE_FILES_TOOL: ToolContract;
|
|
3
4
|
export declare function registerReadMultipleFilesTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -5,7 +5,8 @@ import { readMultipleFiles } from '../lib/file-operations/read-multiple-files.js
|
|
|
5
5
|
import { ReadMultipleFilesInputSchema, ReadMultipleFilesOutputSchema, } from '../schemas.js';
|
|
6
6
|
import { buildResourceLink, buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, maybeExternalizeTextContent, READ_ONLY_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
7
7
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
8
|
-
const READ_MULTIPLE_FILES_TOOL = {
|
|
8
|
+
export const READ_MULTIPLE_FILES_TOOL = {
|
|
9
|
+
name: 'read_many',
|
|
9
10
|
title: 'Read Multiple Files',
|
|
10
11
|
description: 'Read multiple text files in a single request. ' +
|
|
11
12
|
'Returns contents and metadata for each file. ' +
|
|
@@ -13,6 +14,10 @@ const READ_MULTIPLE_FILES_TOOL = {
|
|
|
13
14
|
inputSchema: ReadMultipleFilesInputSchema,
|
|
14
15
|
outputSchema: ReadMultipleFilesOutputSchema,
|
|
15
16
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
17
|
+
nuances: ['Total read budget is capped by `MAX_READ_MANY_TOTAL_SIZE`.'],
|
|
18
|
+
gotchas: [
|
|
19
|
+
'Per-file `truncationReason` can be `head`, `range`, or `externalized`.',
|
|
20
|
+
],
|
|
16
21
|
};
|
|
17
22
|
async function handleReadMultipleFiles(args, signal, resourceStore) {
|
|
18
23
|
const options = {
|
|
@@ -121,8 +126,7 @@ export function registerReadMultipleFilesTool(server, options = {}) {
|
|
|
121
126
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_NOT_FILE, primaryPath),
|
|
122
127
|
});
|
|
123
128
|
};
|
|
124
|
-
const
|
|
125
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
129
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
126
130
|
guard: options.isInitialized,
|
|
127
131
|
progressMessage: (args) => {
|
|
128
132
|
const first = path.basename(args.paths[0] ?? '');
|
|
@@ -143,7 +147,8 @@ export function registerReadMultipleFilesTool(server, options = {}) {
|
|
|
143
147
|
return `🕮 read_many: ${total} files read`;
|
|
144
148
|
},
|
|
145
149
|
});
|
|
146
|
-
|
|
150
|
+
const validatedHandler = withValidatedArgs(ReadMultipleFilesInputSchema, wrappedHandler);
|
|
151
|
+
if (registerToolTaskIfAvailable(server, 'read_many', READ_MULTIPLE_FILES_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
147
152
|
return;
|
|
148
|
-
server.registerTool('read_many', withDefaultIcons({ ...READ_MULTIPLE_FILES_TOOL }, options.iconInfo),
|
|
153
|
+
server.registerTool('read_many', withDefaultIcons({ ...READ_MULTIPLE_FILES_TOOL }, options.iconInfo), validatedHandler);
|
|
149
154
|
}
|
package/dist/tools/read.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const READ_FILE_TOOL: ToolContract;
|
|
3
4
|
export declare function registerReadFileTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
package/dist/tools/read.js
CHANGED
|
@@ -5,7 +5,8 @@ import { readFile } from '../lib/fs-helpers.js';
|
|
|
5
5
|
import { ReadFileInputSchema, ReadFileOutputSchema } from '../schemas.js';
|
|
6
6
|
import { buildResourceLink, buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, maybeExternalizeTextContent, READ_ONLY_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
7
7
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
8
|
-
const READ_FILE_TOOL = {
|
|
8
|
+
export const READ_FILE_TOOL = {
|
|
9
|
+
name: 'read',
|
|
9
10
|
title: 'Read File',
|
|
10
11
|
description: 'Read the text contents of a file. ' +
|
|
11
12
|
'Use head parameter to preview the first N lines of large files. ' +
|
|
@@ -13,6 +14,9 @@ const READ_FILE_TOOL = {
|
|
|
13
14
|
inputSchema: ReadFileInputSchema,
|
|
14
15
|
outputSchema: ReadFileOutputSchema,
|
|
15
16
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
17
|
+
nuances: [
|
|
18
|
+
'Large content is externalized to `filesystem-mcp://result/{id}` and preview is returned inline.',
|
|
19
|
+
],
|
|
16
20
|
};
|
|
17
21
|
async function handleReadFile(args, signal, resourceStore) {
|
|
18
22
|
const options = {
|
|
@@ -80,8 +84,7 @@ export function registerReadFileTool(server, options = {}) {
|
|
|
80
84
|
run: (signal) => handleReadFile(args, signal, options.resourceStore),
|
|
81
85
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.E_NOT_FILE, args.path),
|
|
82
86
|
});
|
|
83
|
-
const
|
|
84
|
-
const wrappedHandler = wrapToolHandler(validatedHandler, {
|
|
87
|
+
const wrappedHandler = wrapToolHandler(handler, {
|
|
85
88
|
guard: options.isInitialized,
|
|
86
89
|
progressMessage: (args) => {
|
|
87
90
|
const name = path.basename(args.path);
|
|
@@ -105,7 +108,8 @@ export function registerReadFileTool(server, options = {}) {
|
|
|
105
108
|
return `🕮 read: ${name} • ${sc.totalLines ?? '?'} lines`;
|
|
106
109
|
},
|
|
107
110
|
});
|
|
108
|
-
|
|
111
|
+
const validatedHandler = withValidatedArgs(ReadFileInputSchema, wrappedHandler);
|
|
112
|
+
if (registerToolTaskIfAvailable(server, 'read', READ_FILE_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
109
113
|
return;
|
|
110
|
-
server.registerTool('read', withDefaultIcons({ ...READ_FILE_TOOL }, options.iconInfo),
|
|
114
|
+
server.registerTool('read', withDefaultIcons({ ...READ_FILE_TOOL }, options.iconInfo), validatedHandler);
|
|
111
115
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { type ToolRegistrationOptions } from './shared.js';
|
|
2
|
+
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
|
+
export declare const SEARCH_AND_REPLACE_TOOL: ToolContract;
|
|
3
4
|
export declare function registerSearchAndReplaceTool(server: McpServer, options?: ToolRegistrationOptions): void;
|