@j0hanz/filesystem-mcp 1.4.0 → 1.5.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/dist/config.d.ts +2 -1
- package/dist/lib/constants.d.ts +10 -4
- package/dist/lib/constants.js +11 -1
- package/dist/lib/observability.d.ts +1 -1
- package/dist/resources/generated-instructions.js +14 -30
- package/dist/resources/tool-catalog.d.ts +2 -0
- package/dist/resources/tool-catalog.js +29 -0
- package/dist/resources/tool-info.d.ts +4 -0
- package/dist/resources/tool-info.js +45 -0
- package/dist/resources/workflows.d.ts +1 -0
- package/dist/resources/workflows.js +36 -0
- package/dist/resources.d.ts +2 -0
- package/dist/resources.js +46 -0
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +18 -16
- package/dist/server/bootstrap.js +3 -1
- package/dist/server/capabilities.d.ts +1 -1
- package/dist/server.d.ts +0 -1
- package/dist/tools/calculate-hash.js +1 -1
- package/dist/tools/create-directory.js +2 -2
- package/dist/tools/edit-file.js +10 -3
- package/dist/tools/list-directory.js +1 -1
- package/dist/tools/replace-in-files.js +1 -1
- package/dist/tools/search-content.js +1 -1
- package/dist/tools/shared.d.ts +2 -1
- package/dist/tools/shared.js +3 -1
- package/dist/tools/task-support.d.ts +0 -6
- package/dist/tools/task-support.js +1 -1
- package/package.json +3 -3
package/dist/config.d.ts
CHANGED
|
@@ -112,8 +112,9 @@ export declare const ErrorCode: {
|
|
|
112
112
|
export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
113
113
|
export declare function formatBytes(bytes: number): string;
|
|
114
114
|
export declare function joinLines(lines: readonly string[]): string;
|
|
115
|
-
|
|
115
|
+
interface OperationSummary {
|
|
116
116
|
truncated?: boolean;
|
|
117
117
|
truncatedReason?: string;
|
|
118
118
|
}
|
|
119
119
|
export declare function formatOperationSummary(summary: OperationSummary): string;
|
|
120
|
+
export {};
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
declare const VALID_LOG_LEVELS: readonly ["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"];
|
|
2
|
-
export type ValidLogLevel = (typeof VALID_LOG_LEVELS)[number];
|
|
3
1
|
export declare const DEFAULT_LOG_LEVEL: "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
|
|
4
2
|
export declare const PARALLEL_CONCURRENCY: number;
|
|
5
3
|
export declare const MAX_SEARCHABLE_FILE_SIZE: number;
|
|
@@ -13,8 +11,17 @@ export declare const DEFAULT_SEARCH_TIMEOUT_MS: number;
|
|
|
13
11
|
*/
|
|
14
12
|
export declare const SEARCH_WORKERS: number;
|
|
15
13
|
export declare const DEFAULT_MAX_DEPTH = 10;
|
|
16
|
-
export declare const DEFAULT_LIST_MAX_ENTRIES =
|
|
14
|
+
export declare const DEFAULT_LIST_MAX_ENTRIES = 20000;
|
|
17
15
|
export declare const DEFAULT_SEARCH_MAX_FILES = 20000;
|
|
16
|
+
export declare const MAX_TREE_DEPTH = 50;
|
|
17
|
+
export declare const DEFAULT_TREE_DEPTH = 5;
|
|
18
|
+
export declare const MAX_TREE_ENTRIES = 20000;
|
|
19
|
+
export declare const DEFAULT_TREE_ENTRIES = 1000;
|
|
20
|
+
export declare const MAX_LIST_ENTRIES = 20000;
|
|
21
|
+
export declare const MAX_SEARCH_RESULTS = 10000;
|
|
22
|
+
export declare const DEFAULT_SEARCH_RESULTS = 100;
|
|
23
|
+
export declare const MAX_SEARCH_DEPTH = 100;
|
|
24
|
+
export declare const DEFAULT_SEARCH_CONTENT_RESULTS = 500;
|
|
18
25
|
export declare const MAX_LINE_CONTENT_LENGTH = 200;
|
|
19
26
|
export declare const BINARY_CHECK_BUFFER_SIZE = 512;
|
|
20
27
|
export declare const SENSITIVE_FILE_DENYLIST: string[];
|
|
@@ -22,4 +29,3 @@ export declare const SENSITIVE_FILE_ALLOWLIST: string[];
|
|
|
22
29
|
export declare const KNOWN_BINARY_EXTENSIONS: Set<string>;
|
|
23
30
|
export declare const DEFAULT_EXCLUDE_PATTERNS: string[];
|
|
24
31
|
export declare function getMimeType(ext: string): string;
|
|
25
|
-
export {};
|
package/dist/lib/constants.js
CHANGED
|
@@ -109,8 +109,18 @@ const ENV_ALLOWLIST = parseEnvList('FS_CONTEXT_ALLOWLIST');
|
|
|
109
109
|
export const SEARCH_WORKERS = parseEnvInt('FS_CONTEXT_SEARCH_WORKERS', getDefaultSearchWorkers(), 1, 16);
|
|
110
110
|
// Hardcoded defaults
|
|
111
111
|
export const DEFAULT_MAX_DEPTH = 10;
|
|
112
|
-
export const DEFAULT_LIST_MAX_ENTRIES =
|
|
112
|
+
export const DEFAULT_LIST_MAX_ENTRIES = 20000;
|
|
113
113
|
export const DEFAULT_SEARCH_MAX_FILES = 20000;
|
|
114
|
+
// Schema limits and defaults
|
|
115
|
+
export const MAX_TREE_DEPTH = 50;
|
|
116
|
+
export const DEFAULT_TREE_DEPTH = 5;
|
|
117
|
+
export const MAX_TREE_ENTRIES = 20000;
|
|
118
|
+
export const DEFAULT_TREE_ENTRIES = 1000;
|
|
119
|
+
export const MAX_LIST_ENTRIES = 20000;
|
|
120
|
+
export const MAX_SEARCH_RESULTS = 10000;
|
|
121
|
+
export const DEFAULT_SEARCH_RESULTS = 100;
|
|
122
|
+
export const MAX_SEARCH_DEPTH = 100;
|
|
123
|
+
export const DEFAULT_SEARCH_CONTENT_RESULTS = 500;
|
|
114
124
|
// Non-configurable constants
|
|
115
125
|
export const MAX_LINE_CONTENT_LENGTH = 200;
|
|
116
126
|
export const BINARY_CHECK_BUFFER_SIZE = 512;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildToolCatalogDetailsOnly } from './tool-catalog.js';
|
|
2
|
+
import { buildCoreContextPack, getSharedConstraints, getToolContracts, } from './tool-info.js';
|
|
3
|
+
import { buildWorkflowGuide } from './workflows.js';
|
|
2
4
|
const INSTRUCTIONS_HEADER = `# FILESYSTEM-MCP INSTRUCTIONS
|
|
3
5
|
|
|
4
6
|
> Resource: \`internal://instructions\` | Prompt: \`get-help\`
|
|
@@ -24,39 +26,15 @@ const INSTRUCTIONS_HEADER = `# FILESYSTEM-MCP INSTRUCTIONS
|
|
|
24
26
|
|
|
25
27
|
## GOLDEN PATH WORKFLOWS
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
1. \`roots\` (List allowed paths).
|
|
29
|
-
2. \`ls\` (files) | \`tree\` (structure).
|
|
30
|
-
3. \`stat\` | \`stat_many\` (size/type check).
|
|
31
|
-
4. \`read\` | \`read_many\` (content).
|
|
32
|
-
> **Strict:** Never guess paths. Resolve first.
|
|
29
|
+
See "Workflow Reference" below for detailed execution sequences.
|
|
33
30
|
|
|
34
|
-
### B: SEARCH
|
|
35
|
-
1. \`find\` (glob candidates).
|
|
36
|
-
2. \`grep\` (content search).
|
|
37
|
-
3. \`read\` (verify context).
|
|
38
|
-
> **Tip:** Content search requires \`grep\`, not \`find\`.
|
|
39
|
-
|
|
40
|
-
### C: EDIT
|
|
41
|
-
1. \`edit\` (precise string match).
|
|
42
|
-
2. \`search_and_replace\` (bulk regex/glob).
|
|
43
|
-
3. \`mv\` | \`rm\` (file layout).
|
|
44
|
-
4. \`mkdir\` (create dirs).
|
|
45
|
-
> **Strict:** Confirm destructive ops (\`write\`, \`mv\`, \`rm\`, bulk replace).
|
|
46
|
-
|
|
47
|
-
### D: PATCH
|
|
48
|
-
1. \`diff_files\` (generate).
|
|
49
|
-
2. \`apply_patch\` (dryRun: true).
|
|
50
|
-
3. \`apply_patch\` (dryRun: false).
|
|
51
|
-
> **Tip:** Use \`diff_files\` output directly.
|
|
52
31
|
`;
|
|
53
32
|
const INSTRUCTIONS_FOOTER = `
|
|
54
33
|
## CONSTRAINTS
|
|
55
34
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- **Cache:** Externalized results are ephemeral (in-memory).
|
|
35
|
+
${getSharedConstraints()
|
|
36
|
+
.map((c) => `- ${c}`)
|
|
37
|
+
.join('\n')}
|
|
60
38
|
|
|
61
39
|
## ERROR HANDLING
|
|
62
40
|
|
|
@@ -87,13 +65,19 @@ function formatToolSection(tool) {
|
|
|
87
65
|
return parts.join('\n');
|
|
88
66
|
}
|
|
89
67
|
export function buildServerInstructions() {
|
|
90
|
-
const toolSections =
|
|
68
|
+
const toolSections = getToolContracts().map(formatToolSection).join('\n\n');
|
|
91
69
|
return [
|
|
92
70
|
INSTRUCTIONS_HEADER,
|
|
71
|
+
buildCoreContextPack(),
|
|
72
|
+
'',
|
|
73
|
+
buildToolCatalogDetailsOnly(),
|
|
74
|
+
'',
|
|
93
75
|
'## TOOL REFERENCE',
|
|
94
76
|
'',
|
|
95
77
|
toolSections,
|
|
96
78
|
'',
|
|
79
|
+
buildWorkflowGuide(),
|
|
80
|
+
'',
|
|
97
81
|
'---',
|
|
98
82
|
INSTRUCTIONS_FOOTER,
|
|
99
83
|
].join('\n');
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { buildCoreContextPack } from './tool-info.js';
|
|
2
|
+
const CATALOG_GUIDE = `## Tool Catalog Details
|
|
3
|
+
|
|
4
|
+
## Cross-Tool Data Flow
|
|
5
|
+
|
|
6
|
+
\`\`\`
|
|
7
|
+
find -> output_paths -> grep.paths
|
|
8
|
+
diff_files -> output_patch -> apply_patch.patch
|
|
9
|
+
\`\`\`
|
|
10
|
+
|
|
11
|
+
## Search Strategy Strategy
|
|
12
|
+
|
|
13
|
+
- Use \`find\` for glob-based file discovery.
|
|
14
|
+
- Use \`grep\` for content-based searches.
|
|
15
|
+
- Use \`search_and_replace\` ONLY for bulk replacements, not for discovery.
|
|
16
|
+
|
|
17
|
+
## Patch Management
|
|
18
|
+
|
|
19
|
+
- Always generate a patch with \`diff_files\` first.
|
|
20
|
+
- Always use \`dryRun: true\` with \`apply_patch\` to verify changes.
|
|
21
|
+
- \`apply_patch\` works on unified diff format.
|
|
22
|
+
`;
|
|
23
|
+
export function buildToolCatalog() {
|
|
24
|
+
// Return combined view for standalone resource usage
|
|
25
|
+
return `${buildCoreContextPack()}\n\n${CATALOG_GUIDE}`;
|
|
26
|
+
}
|
|
27
|
+
export function buildToolCatalogDetailsOnly() {
|
|
28
|
+
return CATALOG_GUIDE;
|
|
29
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DEFAULT_SEARCH_CONTENT_RESULTS, MAX_SEARCH_RESULTS, MAX_TEXT_FILE_SIZE, } from '../lib/constants.js';
|
|
2
|
+
import { ALL_TOOLS } from '../tools.js';
|
|
3
|
+
function toEntry(contract) {
|
|
4
|
+
const annotations = [];
|
|
5
|
+
if (contract.annotations?.destructiveHint)
|
|
6
|
+
annotations.push('[Destructive]');
|
|
7
|
+
if (contract.annotations?.idempotentHint)
|
|
8
|
+
annotations.push('[Idempotent]');
|
|
9
|
+
if (contract.annotations?.readOnlyHint)
|
|
10
|
+
annotations.push('[Read-Only]');
|
|
11
|
+
return {
|
|
12
|
+
name: contract.name,
|
|
13
|
+
description: contract.description,
|
|
14
|
+
...(annotations.length > 0 ? { annotations } : {}),
|
|
15
|
+
...(contract.nuances && contract.nuances.length > 0
|
|
16
|
+
? { nuances: contract.nuances }
|
|
17
|
+
: {}),
|
|
18
|
+
...(contract.gotchas && contract.gotchas.length > 0
|
|
19
|
+
? { gotchas: contract.gotchas }
|
|
20
|
+
: {}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const ENTRIES = Object.fromEntries(ALL_TOOLS.map((contract) => [contract.name, toEntry(contract)]));
|
|
24
|
+
export function getToolContracts() {
|
|
25
|
+
return ALL_TOOLS;
|
|
26
|
+
}
|
|
27
|
+
export function buildCoreContextPack() {
|
|
28
|
+
const names = Object.keys(ENTRIES).sort((a, b) => a.localeCompare(b));
|
|
29
|
+
const rows = names.map((name) => {
|
|
30
|
+
const e = ENTRIES[name];
|
|
31
|
+
if (!e)
|
|
32
|
+
return '';
|
|
33
|
+
const annotations = e.annotations ? ` ${e.annotations.join(' ')}` : '';
|
|
34
|
+
return `| \`${e.name}\` | ${e.description}${annotations} |`;
|
|
35
|
+
});
|
|
36
|
+
return `## Core Context Pack\n\n| Tool | Purpose |\n|------|---------|\n${rows.join('\n')}`;
|
|
37
|
+
}
|
|
38
|
+
export function getSharedConstraints() {
|
|
39
|
+
return [
|
|
40
|
+
'Allowed roots only (negotiated via CLI).',
|
|
41
|
+
'Sensitive files denylisted by default.',
|
|
42
|
+
`Max file size (${Math.floor(MAX_TEXT_FILE_SIZE / 1024 / 1024)}MB) & search results (${MAX_SEARCH_RESULTS} files, ${DEFAULT_SEARCH_CONTENT_RESULTS} lines) enforced.`,
|
|
43
|
+
'Externalized results are ephemeral (in-memory).',
|
|
44
|
+
];
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildWorkflowGuide(): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getSharedConstraints } from './tool-info.js';
|
|
2
|
+
export function buildWorkflowGuide() {
|
|
3
|
+
return `## Workflow Reference
|
|
4
|
+
|
|
5
|
+
### A: EXPLORE
|
|
6
|
+
1. \`roots\` (List allowed paths).
|
|
7
|
+
2. \`ls\` (files) | \`tree\` (structure).
|
|
8
|
+
3. \`stat\` | \`stat_many\` (size/type check).
|
|
9
|
+
4. \`read\` | \`read_many\` (content).
|
|
10
|
+
> **Strict:** Never guess paths. Resolve first.
|
|
11
|
+
|
|
12
|
+
### B: SEARCH
|
|
13
|
+
1. \`find\` (glob candidates).
|
|
14
|
+
2. \`grep\` (content search).
|
|
15
|
+
3. \`read\` (verify context).
|
|
16
|
+
> **Tip:** Content search requires \`grep\`, not \`find\`.
|
|
17
|
+
|
|
18
|
+
### C: EDIT
|
|
19
|
+
1. \`edit\` (precise string match).
|
|
20
|
+
2. \`search_and_replace\` (bulk regex/glob).
|
|
21
|
+
3. \`mv\` | \`rm\` (file layout).
|
|
22
|
+
4. \`mkdir\` (create dirs).
|
|
23
|
+
> **Strict:** Confirm destructive ops (\`write\`, \`mv\`, \`rm\`, bulk replace).
|
|
24
|
+
|
|
25
|
+
### D: PATCH
|
|
26
|
+
1. \`diff_files\` (generate).
|
|
27
|
+
2. \`apply_patch\` (dryRun: true).
|
|
28
|
+
3. \`apply_patch\` (dryRun: false).
|
|
29
|
+
> **Tip:** Use \`diff_files\` output directly.
|
|
30
|
+
|
|
31
|
+
## Shared Constraints
|
|
32
|
+
${getSharedConstraints()
|
|
33
|
+
.map((c) => `- ${c}`)
|
|
34
|
+
.join('\n')}
|
|
35
|
+
`;
|
|
36
|
+
}
|
package/dist/resources.d.ts
CHANGED
|
@@ -2,5 +2,7 @@ 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;
|
|
5
|
+
export declare function registerToolCatalogResource(server: McpServer, iconInfo?: IconInfo): void;
|
|
6
|
+
export declare function registerWorkflowGuideResource(server: McpServer, iconInfo?: IconInfo): void;
|
|
5
7
|
export declare function registerResultResources(server: McpServer, store: ResourceStore, iconInfo?: IconInfo): void;
|
|
6
8
|
export declare function registerMetricsResource(server: McpServer, iconInfo?: IconInfo): void;
|
package/dist/resources.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
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
|
+
import { buildToolCatalog } from './resources/tool-catalog.js';
|
|
5
|
+
import { buildWorkflowGuide } from './resources/workflows.js';
|
|
4
6
|
import { withDefaultIcons } from './tools/shared.js';
|
|
5
7
|
const RESULT_TEMPLATE = new ResourceTemplate('filesystem-mcp://result/{id}', {
|
|
6
8
|
list: undefined,
|
|
@@ -13,6 +15,12 @@ const RESULT_RESOURCE_DESCRIPTION = 'Ephemeral cached tool output exposed as an
|
|
|
13
15
|
const METRICS_RESOURCE_NAME = 'filesystem-mcp-metrics';
|
|
14
16
|
const METRICS_RESOURCE_URI = 'filesystem-mcp://metrics';
|
|
15
17
|
const METRICS_RESOURCE_DESCRIPTION = 'Live per-tool call/error/avgDurationMs metrics snapshot.';
|
|
18
|
+
const CATALOG_RESOURCE_NAME = 'filesystem-mcp-catalog';
|
|
19
|
+
const CATALOG_RESOURCE_URI = 'internal://tool-catalog';
|
|
20
|
+
const CATALOG_RESOURCE_DESCRIPTION = 'Detailed catalog of tools and their inter-dependencies.';
|
|
21
|
+
const WORKFLOW_RESOURCE_NAME = 'filesystem-mcp-workflows';
|
|
22
|
+
const WORKFLOW_RESOURCE_URI = 'internal://workflows';
|
|
23
|
+
const WORKFLOW_RESOURCE_DESCRIPTION = 'Recommended workflows for common tasks.';
|
|
16
24
|
export function registerInstructionResource(server, instructions, iconInfo) {
|
|
17
25
|
server.registerResource(INSTRUCTIONS_RESOURCE_NAME, INSTRUCTIONS_RESOURCE_URI, withDefaultIcons({
|
|
18
26
|
title: 'Server Instructions',
|
|
@@ -32,6 +40,44 @@ export function registerInstructionResource(server, instructions, iconInfo) {
|
|
|
32
40
|
],
|
|
33
41
|
}));
|
|
34
42
|
}
|
|
43
|
+
export function registerToolCatalogResource(server, iconInfo) {
|
|
44
|
+
server.registerResource(CATALOG_RESOURCE_NAME, CATALOG_RESOURCE_URI, withDefaultIcons({
|
|
45
|
+
title: 'Tool Catalog',
|
|
46
|
+
description: CATALOG_RESOURCE_DESCRIPTION,
|
|
47
|
+
mimeType: 'text/markdown',
|
|
48
|
+
annotations: {
|
|
49
|
+
audience: ['assistant'],
|
|
50
|
+
priority: 0.6,
|
|
51
|
+
},
|
|
52
|
+
}, iconInfo), (uri) => ({
|
|
53
|
+
contents: [
|
|
54
|
+
{
|
|
55
|
+
uri: uri.href,
|
|
56
|
+
mimeType: 'text/markdown',
|
|
57
|
+
text: buildToolCatalog(),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
export function registerWorkflowGuideResource(server, iconInfo) {
|
|
63
|
+
server.registerResource(WORKFLOW_RESOURCE_NAME, WORKFLOW_RESOURCE_URI, withDefaultIcons({
|
|
64
|
+
title: 'Workflow Guide',
|
|
65
|
+
description: WORKFLOW_RESOURCE_DESCRIPTION,
|
|
66
|
+
mimeType: 'text/markdown',
|
|
67
|
+
annotations: {
|
|
68
|
+
audience: ['assistant'],
|
|
69
|
+
priority: 0.7,
|
|
70
|
+
},
|
|
71
|
+
}, iconInfo), (uri) => ({
|
|
72
|
+
contents: [
|
|
73
|
+
{
|
|
74
|
+
uri: uri.href,
|
|
75
|
+
mimeType: 'text/markdown',
|
|
76
|
+
text: buildWorkflowGuide(),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
35
81
|
export function registerResultResources(server, store, iconInfo) {
|
|
36
82
|
server.registerResource(RESULT_RESOURCE_NAME, RESULT_TEMPLATE, withDefaultIcons({
|
|
37
83
|
title: 'Cached Tool Result',
|
package/dist/schemas.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare const ListDirectoryInputSchema: z.ZodObject<{
|
|
|
38
38
|
includeHidden: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
39
39
|
includeIgnored: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
40
40
|
maxDepth: z.ZodOptional<z.ZodNumber>;
|
|
41
|
-
maxEntries: z.ZodOptional<z.ZodNumber
|
|
41
|
+
maxEntries: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
42
42
|
sortBy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
43
43
|
name: "name";
|
|
44
44
|
size: "size";
|
package/dist/schemas.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { DEFAULT_LIST_MAX_ENTRIES, DEFAULT_SEARCH_CONTENT_RESULTS, DEFAULT_SEARCH_RESULTS, DEFAULT_TREE_DEPTH, DEFAULT_TREE_ENTRIES, MAX_LIST_ENTRIES, MAX_SEARCH_DEPTH, MAX_SEARCH_RESULTS, MAX_TREE_DEPTH, MAX_TREE_ENTRIES, } from './lib/constants.js';
|
|
2
3
|
import { ErrorCode } from './lib/errors.js';
|
|
3
4
|
function isSafeGlobPattern(value) {
|
|
4
5
|
if (value.length === 0)
|
|
@@ -111,16 +112,17 @@ export const ListDirectoryInputSchema = z.strictObject({
|
|
|
111
112
|
.number()
|
|
112
113
|
.int({ error: 'Must be integer' })
|
|
113
114
|
.min(1, 'Min: 1')
|
|
114
|
-
.max(
|
|
115
|
+
.max(MAX_TREE_DEPTH, `Max: ${MAX_TREE_DEPTH}`)
|
|
115
116
|
.optional()
|
|
116
117
|
.describe('Max recursion depth when pattern is provided'),
|
|
117
118
|
maxEntries: z
|
|
118
119
|
.number()
|
|
119
120
|
.int({ error: 'Must be integer' })
|
|
120
121
|
.min(1, 'Min: 1')
|
|
121
|
-
.max(
|
|
122
|
+
.max(MAX_LIST_ENTRIES, `Max: ${MAX_LIST_ENTRIES}`)
|
|
122
123
|
.optional()
|
|
123
|
-
.
|
|
124
|
+
.default(DEFAULT_LIST_MAX_ENTRIES)
|
|
125
|
+
.describe(`Maximum entries to return before truncation. Default: ${DEFAULT_LIST_MAX_ENTRIES}`),
|
|
124
126
|
sortBy: ListDirectorySortSchema.optional()
|
|
125
127
|
.default('name')
|
|
126
128
|
.describe('Sort field (name, size, modified, type)'),
|
|
@@ -157,10 +159,10 @@ export const SearchFilesInputSchema = z.strictObject({
|
|
|
157
159
|
.number()
|
|
158
160
|
.int({ error: 'Must be integer' })
|
|
159
161
|
.min(1, 'Min: 1')
|
|
160
|
-
.max(
|
|
162
|
+
.max(MAX_SEARCH_RESULTS, `Max: ${MAX_SEARCH_RESULTS}`)
|
|
161
163
|
.optional()
|
|
162
|
-
.default(
|
|
163
|
-
.describe(
|
|
164
|
+
.default(DEFAULT_SEARCH_RESULTS)
|
|
165
|
+
.describe(`Max results (1-${MAX_SEARCH_RESULTS}). Default: ${DEFAULT_SEARCH_RESULTS}`),
|
|
164
166
|
includeIgnored: z
|
|
165
167
|
.boolean()
|
|
166
168
|
.optional()
|
|
@@ -178,7 +180,7 @@ export const SearchFilesInputSchema = z.strictObject({
|
|
|
178
180
|
.number()
|
|
179
181
|
.int({ error: 'Must be integer' })
|
|
180
182
|
.min(0, 'Min: 0')
|
|
181
|
-
.max(
|
|
183
|
+
.max(MAX_SEARCH_DEPTH, `Max: ${MAX_SEARCH_DEPTH}`)
|
|
182
184
|
.optional()
|
|
183
185
|
.describe('Maximum directory depth to scan'),
|
|
184
186
|
cursor: z
|
|
@@ -192,18 +194,18 @@ export const TreeInputSchema = z.strictObject({
|
|
|
192
194
|
.number()
|
|
193
195
|
.int({ error: 'Must be integer' })
|
|
194
196
|
.min(0, 'Min: 0')
|
|
195
|
-
.max(
|
|
197
|
+
.max(MAX_TREE_DEPTH, `Max: ${MAX_TREE_DEPTH}`)
|
|
196
198
|
.optional()
|
|
197
|
-
.default(
|
|
198
|
-
.describe(
|
|
199
|
+
.default(DEFAULT_TREE_DEPTH)
|
|
200
|
+
.describe(`Depth (0=root node only, no children). Default: ${DEFAULT_TREE_DEPTH}`),
|
|
199
201
|
maxEntries: z
|
|
200
202
|
.number()
|
|
201
203
|
.int({ error: 'Must be integer' })
|
|
202
204
|
.min(1, 'Min: 1')
|
|
203
|
-
.max(
|
|
205
|
+
.max(MAX_TREE_ENTRIES, `Max: ${MAX_TREE_ENTRIES}`)
|
|
204
206
|
.optional()
|
|
205
|
-
.default(
|
|
206
|
-
.describe(
|
|
207
|
+
.default(DEFAULT_TREE_ENTRIES)
|
|
208
|
+
.describe(`Max entries. Default: ${DEFAULT_TREE_ENTRIES}`),
|
|
207
209
|
includeHidden: z
|
|
208
210
|
.boolean()
|
|
209
211
|
.optional()
|
|
@@ -249,10 +251,10 @@ export const SearchContentInputSchema = z.strictObject({
|
|
|
249
251
|
.number()
|
|
250
252
|
.int({ error: 'Must be integer' })
|
|
251
253
|
.min(0, 'Min: 0')
|
|
252
|
-
.max(
|
|
254
|
+
.max(MAX_SEARCH_RESULTS, `Max: ${MAX_SEARCH_RESULTS}`)
|
|
253
255
|
.optional()
|
|
254
|
-
.default(
|
|
255
|
-
.describe(
|
|
256
|
+
.default(DEFAULT_SEARCH_CONTENT_RESULTS)
|
|
257
|
+
.describe(`Maximum match rows to return. Default: ${DEFAULT_SEARCH_CONTENT_RESULTS}`),
|
|
256
258
|
filePattern: z
|
|
257
259
|
.string()
|
|
258
260
|
.min(1, 'Pattern required')
|
package/dist/server/bootstrap.js
CHANGED
|
@@ -12,7 +12,7 @@ import { formatUnknownErrorMessage } from '../lib/errors.js';
|
|
|
12
12
|
import { createInMemoryResourceStore } from '../lib/resource-store.js';
|
|
13
13
|
import { pkgInfo } from '../pkg-info.js';
|
|
14
14
|
import { registerGetHelpPrompt } from '../prompts.js';
|
|
15
|
-
import { registerInstructionResource, registerMetricsResource, registerResultResources, } from '../resources.js';
|
|
15
|
+
import { registerInstructionResource, registerMetricsResource, registerResultResources, registerToolCatalogResource, registerWorkflowGuideResource, } from '../resources.js';
|
|
16
16
|
import { buildServerInstructions } from '../resources/generated-instructions.js';
|
|
17
17
|
import { registerAllTools } from '../tools.js';
|
|
18
18
|
import { withDefaultIcons } from '../tools/shared.js';
|
|
@@ -86,6 +86,8 @@ export async function createServer(options = {}) {
|
|
|
86
86
|
return {};
|
|
87
87
|
});
|
|
88
88
|
registerInstructionResource(server, serverInstructions, localIcon);
|
|
89
|
+
registerToolCatalogResource(server, localIcon);
|
|
90
|
+
registerWorkflowGuideResource(server, localIcon);
|
|
89
91
|
registerGetHelpPrompt(server, serverInstructions, localIcon);
|
|
90
92
|
registerResultResources(server, resourceStore, localIcon);
|
|
91
93
|
registerMetricsResource(server, localIcon);
|
package/dist/server.d.ts
CHANGED
|
@@ -171,7 +171,7 @@ export function registerCalculateHashTool(server, options = {}) {
|
|
|
171
171
|
baseReporter({
|
|
172
172
|
current,
|
|
173
173
|
...(total !== undefined ? { total } : {}),
|
|
174
|
-
message: `🕮 calculate_hash: ${baseName}
|
|
174
|
+
message: `🕮 calculate_hash: ${baseName} [${current} ${fileWord} hashed]`,
|
|
175
175
|
});
|
|
176
176
|
};
|
|
177
177
|
try {
|
|
@@ -34,11 +34,11 @@ export function registerCreateDirectoryTool(server, options = {}) {
|
|
|
34
34
|
const wrappedHandler = wrapToolHandler(handler, {
|
|
35
35
|
guard: options.isInitialized,
|
|
36
36
|
progressMessage: (args) => {
|
|
37
|
-
const name = path.basename(args.path) ||
|
|
37
|
+
const name = path.basename(args.path) || '.';
|
|
38
38
|
return `🛠 mkdir: ${name}`;
|
|
39
39
|
},
|
|
40
40
|
completionMessage: (args, result) => {
|
|
41
|
-
const name = path.basename(args.path) ||
|
|
41
|
+
const name = path.basename(args.path) || '.';
|
|
42
42
|
if (result.isError)
|
|
43
43
|
return `🛠 mkdir: ${name} • failed`;
|
|
44
44
|
return `🛠 mkdir: ${name} • created`;
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -96,7 +96,8 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
96
96
|
guard: options.isInitialized,
|
|
97
97
|
progressMessage: (args) => {
|
|
98
98
|
const name = path.basename(args.path);
|
|
99
|
-
|
|
99
|
+
const dryTag = args.dryRun ? ' [dry run]' : '';
|
|
100
|
+
return `🛠 edit: ${name} [${args.edits.length} edits]${dryTag}`;
|
|
100
101
|
},
|
|
101
102
|
completionMessage: (args, result) => {
|
|
102
103
|
const name = path.basename(args.path);
|
|
@@ -105,10 +106,16 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
105
106
|
const sc = result.structuredContent;
|
|
106
107
|
if (!sc.ok)
|
|
107
108
|
return `🛠 edit: ${name} • failed`;
|
|
109
|
+
const applied = sc.appliedEdits ?? 0;
|
|
110
|
+
const unmatched = sc.unmatchedEdits?.length ?? 0;
|
|
111
|
+
const dryPrefix = args.dryRun ? 'dry run — ' : '';
|
|
112
|
+
if (unmatched > 0) {
|
|
113
|
+
return `🛠 edit: ${name} • ${dryPrefix}${applied} applied, ${unmatched} unmatched`;
|
|
114
|
+
}
|
|
108
115
|
if (sc.lineRange) {
|
|
109
|
-
return `🛠 edit: ${name} •
|
|
116
|
+
return `🛠 edit: ${name} • ${dryPrefix}lines ${sc.lineRange[0]}–${sc.lineRange[1]}`;
|
|
110
117
|
}
|
|
111
|
-
return `🛠 edit: ${name} •
|
|
118
|
+
return `🛠 edit: ${name} • ${dryPrefix}${applied} applied`;
|
|
112
119
|
},
|
|
113
120
|
});
|
|
114
121
|
const validatedHandler = withValidatedArgs(EditFileInputSchema, wrappedHandler);
|
|
@@ -98,7 +98,7 @@ function decodeCursor(cursor) {
|
|
|
98
98
|
async function handleListDirectory(args, signal) {
|
|
99
99
|
const dirPath = resolvePathOrRoot(args.path);
|
|
100
100
|
const cursorOffset = args.cursor !== undefined ? decodeCursor(args.cursor) : 0;
|
|
101
|
-
const pageSize = args.maxEntries
|
|
101
|
+
const pageSize = args.maxEntries;
|
|
102
102
|
const options = {
|
|
103
103
|
includeHidden: args.includeHidden,
|
|
104
104
|
excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,
|
|
@@ -265,7 +265,7 @@ export function registerSearchAndReplaceTool(server, options = {}) {
|
|
|
265
265
|
baseReporter({
|
|
266
266
|
current,
|
|
267
267
|
...(total !== undefined ? { total } : {}),
|
|
268
|
-
message: `🛠 search_and_replace:
|
|
268
|
+
message: `🛠 search_and_replace: ${args.searchPattern} [${current} files processed]`,
|
|
269
269
|
});
|
|
270
270
|
};
|
|
271
271
|
try {
|
|
@@ -220,7 +220,7 @@ export function registerSearchContentTool(server, options = {}) {
|
|
|
220
220
|
baseReporter({
|
|
221
221
|
current,
|
|
222
222
|
...(total !== undefined ? { total } : {}),
|
|
223
|
-
message: `🔎︎ grep: ${pattern}
|
|
223
|
+
message: `🔎︎ grep: ${pattern} [${current} ${fileWord} scanned]`,
|
|
224
224
|
});
|
|
225
225
|
};
|
|
226
226
|
try {
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { type ToolContract } from './contract.js';
|
|
|
8
8
|
export declare const READ_ONLY_TOOL_ANNOTATIONS: {
|
|
9
9
|
readonly readOnlyHint: true;
|
|
10
10
|
readonly idempotentHint: true;
|
|
11
|
+
readonly destructiveHint: false;
|
|
11
12
|
readonly openWorldHint: false;
|
|
12
13
|
};
|
|
13
14
|
export declare const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS: {
|
|
@@ -18,6 +19,7 @@ export declare const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS: {
|
|
|
18
19
|
export declare const IDEMPOTENT_WRITE_TOOL_ANNOTATIONS: {
|
|
19
20
|
readonly readOnlyHint: false;
|
|
20
21
|
readonly idempotentHint: true;
|
|
22
|
+
readonly destructiveHint: false;
|
|
21
23
|
readonly openWorldHint: false;
|
|
22
24
|
};
|
|
23
25
|
export declare function shouldStripStructuredOutput(): boolean;
|
|
@@ -48,7 +50,6 @@ interface ToolErrorResponse extends Record<string, unknown> {
|
|
|
48
50
|
isError: true;
|
|
49
51
|
}
|
|
50
52
|
export type ToolResult<T> = ToolResponse<T> | ToolErrorResponse;
|
|
51
|
-
export declare function parseToolArgs<Schema extends z.ZodType>(schema: Schema, args: unknown): z.infer<Schema>;
|
|
52
53
|
export declare function withValidatedArgs<Args, Result>(schema: z.ZodType<Args>, handler: (args: Args, extra: ToolExtra) => Promise<ToolResult<Result>>): (args: unknown, extra: ToolExtra) => Promise<ToolResult<Result>>;
|
|
53
54
|
type ProgressToken = string | number;
|
|
54
55
|
export interface ToolExtra {
|
package/dist/tools/shared.js
CHANGED
|
@@ -18,6 +18,7 @@ function publishContextDiagnostics(event) {
|
|
|
18
18
|
export const READ_ONLY_TOOL_ANNOTATIONS = {
|
|
19
19
|
readOnlyHint: true,
|
|
20
20
|
idempotentHint: true,
|
|
21
|
+
destructiveHint: false,
|
|
21
22
|
openWorldHint: false,
|
|
22
23
|
};
|
|
23
24
|
export const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS = {
|
|
@@ -28,6 +29,7 @@ export const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS = {
|
|
|
28
29
|
export const IDEMPOTENT_WRITE_TOOL_ANNOTATIONS = {
|
|
29
30
|
readOnlyHint: false,
|
|
30
31
|
idempotentHint: true,
|
|
32
|
+
destructiveHint: false,
|
|
31
33
|
openWorldHint: false,
|
|
32
34
|
};
|
|
33
35
|
export function shouldStripStructuredOutput() {
|
|
@@ -107,7 +109,7 @@ function resolveDetailedError(error, defaultCode, path) {
|
|
|
107
109
|
export function buildToolResponse(text, structuredContent, extraContent = []) {
|
|
108
110
|
return buildContentBlock(text, structuredContent, extraContent);
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
function parseToolArgs(schema, args) {
|
|
111
113
|
const candidate = args === undefined ? {} : args;
|
|
112
114
|
const parsed = schema.safeParse(candidate);
|
|
113
115
|
if (parsed.success) {
|
|
@@ -9,12 +9,6 @@ type TaskToolExtra = ToolExtra & {
|
|
|
9
9
|
taskRequestedTtl?: number | null;
|
|
10
10
|
};
|
|
11
11
|
type ToolArgs<Args extends ZodRawShapeCompat | AnySchema | undefined> = Args extends ZodRawShapeCompat ? ShapeOutput<Args> : Args extends AnySchema ? SchemaOutput<Args> : undefined;
|
|
12
|
-
/**
|
|
13
|
-
* Registers a tool preferring task-capable registration when available, and
|
|
14
|
-
* returns `true`. Returns `false` so the caller can fall through to standard
|
|
15
|
-
* `server.registerTool`.
|
|
16
|
-
*/
|
|
17
|
-
export declare function tryRegisterToolTask<Args extends ZodRawShapeCompat | AnySchema | undefined>(server: McpServer, toolName: string, toolDef: object, taskHandler: ToolTaskHandler<Args>, iconInfo: IconInfo | undefined): boolean;
|
|
18
12
|
export declare function registerToolTaskIfAvailable<Args extends ZodRawShapeCompat | AnySchema | undefined, Result>(server: McpServer, toolName: string, toolDef: object, run: (args: ToolArgs<Args>, extra: TaskToolExtra) => Promise<ToolResult<Result>>, iconInfo: IconInfo | undefined, guard?: () => boolean): boolean;
|
|
19
13
|
export declare function createToolTaskHandler<Result>(run: (args: undefined, extra: TaskToolExtra) => Promise<ToolResult<Result>>, options?: {
|
|
20
14
|
guard?: () => boolean;
|
|
@@ -298,7 +298,7 @@ async function runTaskInBackground(run, args, extra, taskStore, taskId, toolName
|
|
|
298
298
|
* returns `true`. Returns `false` so the caller can fall through to standard
|
|
299
299
|
* `server.registerTool`.
|
|
300
300
|
*/
|
|
301
|
-
|
|
301
|
+
function tryRegisterToolTask(server, toolName, toolDef, taskHandler, iconInfo) {
|
|
302
302
|
if (!hasTaskToolCapability(server))
|
|
303
303
|
return false;
|
|
304
304
|
const tasks = getExperimentalTaskRegistration(server);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j0hanz/filesystem-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"mcpName": "io.github.j0hanz/filesystem-mcp",
|
|
5
5
|
"description": "MCP Server that enables LLMs to interact with the local filesystem.",
|
|
6
6
|
"type": "module",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@eslint/js": "^10.0.1",
|
|
76
|
-
"eslint": "^10.0.
|
|
76
|
+
"eslint": "^10.0.1",
|
|
77
77
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
78
78
|
"@types/node": "^24",
|
|
79
79
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"eslint-plugin-depend": "^1.4.0",
|
|
82
82
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
83
83
|
"jscpd": "^4.0.8",
|
|
84
|
-
"knip": "^5.
|
|
84
|
+
"knip": "^5.85.0",
|
|
85
85
|
"prettier": "^3.8.1",
|
|
86
86
|
"tsx": "^4.21.0",
|
|
87
87
|
"typescript": "^5.9.3",
|