@j0hanz/filesystem-mcp 1.3.2 → 1.5.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.
- package/dist/completions.js +12 -0
- package/dist/config.d.ts +2 -1
- package/dist/lib/constants.d.ts +0 -3
- package/dist/lib/observability.d.ts +1 -1
- package/dist/lib/resource-store.d.ts +1 -0
- package/dist/lib/resource-store.js +4 -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 +44 -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/server/bootstrap.js +3 -1
- package/dist/server/capabilities.d.ts +1 -1
- package/dist/server.d.ts +0 -1
- package/dist/tools/contract.d.ts +4 -0
- package/dist/tools/read-multiple.js +1 -0
- package/dist/tools/replace-in-files.js +1 -0
- package/dist/tools/search-content.js +1 -0
- package/dist/tools/search-files.js +1 -0
- package/dist/tools/shared.d.ts +0 -1
- package/dist/tools/shared.js +58 -36
- package/dist/tools/stat-many.js +1 -0
- package/dist/tools/task-support.d.ts +0 -6
- package/dist/tools/task-support.js +7 -2
- package/dist/tools/tree.js +1 -0
- package/package.json +1 -1
package/dist/completions.js
CHANGED
|
@@ -7,6 +7,7 @@ import { isRecord } from './lib/type-guards.js';
|
|
|
7
7
|
const MAX_COMPLETION_ITEMS = 100;
|
|
8
8
|
const COMPLETION_RATE_LIMIT_MS = 100;
|
|
9
9
|
const completionLastCallMs = new Map();
|
|
10
|
+
const completionLastResult = new Map();
|
|
10
11
|
function extractTopicCompletions(instructions) {
|
|
11
12
|
const headers = [];
|
|
12
13
|
for (const line of instructions.split('\n')) {
|
|
@@ -394,6 +395,16 @@ export function registerCompletions(server, instructions = '') {
|
|
|
394
395
|
const now = Date.now();
|
|
395
396
|
const lastCallMs = completionLastCallMs.get(argName) ?? 0;
|
|
396
397
|
if (now - lastCallMs < COMPLETION_RATE_LIMIT_MS) {
|
|
398
|
+
const lastResult = completionLastResult.get(argName);
|
|
399
|
+
if (lastResult) {
|
|
400
|
+
return {
|
|
401
|
+
completion: {
|
|
402
|
+
values: lastResult.values,
|
|
403
|
+
total: lastResult.total,
|
|
404
|
+
hasMore: lastResult.hasMore,
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
}
|
|
397
408
|
return { completion: { values: [], total: 0, hasMore: false } };
|
|
398
409
|
}
|
|
399
410
|
completionLastCallMs.set(argName, now);
|
|
@@ -403,6 +414,7 @@ export function registerCompletions(server, instructions = '') {
|
|
|
403
414
|
argumentName: argName,
|
|
404
415
|
...(contextArguments ? { contextArguments } : {}),
|
|
405
416
|
});
|
|
417
|
+
completionLastResult.set(argName, completions);
|
|
406
418
|
return {
|
|
407
419
|
completion: {
|
|
408
420
|
values: completions.values,
|
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;
|
|
@@ -22,4 +20,3 @@ export declare const SENSITIVE_FILE_ALLOWLIST: string[];
|
|
|
22
20
|
export declare const KNOWN_BINARY_EXTENSIONS: Set<string>;
|
|
23
21
|
export declare const DEFAULT_EXCLUDE_PATTERNS: string[];
|
|
24
22
|
export declare function getMimeType(ext: string): string;
|
|
25
|
-
export {};
|
|
@@ -147,5 +147,8 @@ export function createInMemoryResourceStore(options = {}) {
|
|
|
147
147
|
bytes: bytesBeforeClear,
|
|
148
148
|
});
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
function keys() {
|
|
151
|
+
return Array.from(byUri.keys());
|
|
152
|
+
}
|
|
153
|
+
return { putText, getText, clear, keys };
|
|
151
154
|
}
|
|
@@ -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,44 @@
|
|
|
1
|
+
import { ALL_TOOLS } from '../tools.js';
|
|
2
|
+
function toEntry(contract) {
|
|
3
|
+
const annotations = [];
|
|
4
|
+
if (contract.annotations?.destructiveHint)
|
|
5
|
+
annotations.push('[Destructive]');
|
|
6
|
+
if (contract.annotations?.idempotentHint)
|
|
7
|
+
annotations.push('[Idempotent]');
|
|
8
|
+
if (contract.annotations?.readOnlyHint)
|
|
9
|
+
annotations.push('[Read-Only]');
|
|
10
|
+
return {
|
|
11
|
+
name: contract.name,
|
|
12
|
+
description: contract.description,
|
|
13
|
+
...(annotations.length > 0 ? { annotations } : {}),
|
|
14
|
+
...(contract.nuances && contract.nuances.length > 0
|
|
15
|
+
? { nuances: contract.nuances }
|
|
16
|
+
: {}),
|
|
17
|
+
...(contract.gotchas && contract.gotchas.length > 0
|
|
18
|
+
? { gotchas: contract.gotchas }
|
|
19
|
+
: {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const ENTRIES = Object.fromEntries(ALL_TOOLS.map((contract) => [contract.name, toEntry(contract)]));
|
|
23
|
+
export function getToolContracts() {
|
|
24
|
+
return ALL_TOOLS;
|
|
25
|
+
}
|
|
26
|
+
export function buildCoreContextPack() {
|
|
27
|
+
const names = Object.keys(ENTRIES).sort((a, b) => a.localeCompare(b));
|
|
28
|
+
const rows = names.map((name) => {
|
|
29
|
+
const e = ENTRIES[name];
|
|
30
|
+
if (!e)
|
|
31
|
+
return '';
|
|
32
|
+
const annotations = e.annotations ? ` ${e.annotations.join(' ')}` : '';
|
|
33
|
+
return `| \`${e.name}\` | ${e.description}${annotations} |`;
|
|
34
|
+
});
|
|
35
|
+
return `## Core Context Pack\n\n| Tool | Purpose |\n|------|---------|\n${rows.join('\n')}`;
|
|
36
|
+
}
|
|
37
|
+
export function getSharedConstraints() {
|
|
38
|
+
return [
|
|
39
|
+
'Allowed roots only (negotiated via CLI).',
|
|
40
|
+
'Sensitive files denylisted by default.',
|
|
41
|
+
'Max file size & search results enforced.',
|
|
42
|
+
'Externalized results are ephemeral (in-memory).',
|
|
43
|
+
];
|
|
44
|
+
}
|
|
@@ -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/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
package/dist/tools/contract.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export const READ_MULTIPLE_FILES_TOOL = {
|
|
|
14
14
|
inputSchema: ReadMultipleFilesInputSchema,
|
|
15
15
|
outputSchema: ReadMultipleFilesOutputSchema,
|
|
16
16
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
17
|
+
taskSupport: 'required',
|
|
17
18
|
nuances: ['Total read budget is capped by `MAX_READ_MANY_TOTAL_SIZE`.'],
|
|
18
19
|
gotchas: [
|
|
19
20
|
'Per-file `truncationReason` can be `head`, `range`, or `externalized`.',
|
|
@@ -23,6 +23,7 @@ export const SEARCH_AND_REPLACE_TOOL = {
|
|
|
23
23
|
inputSchema: SearchAndReplaceInputSchema,
|
|
24
24
|
outputSchema: SearchAndReplaceOutputSchema,
|
|
25
25
|
annotations: DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
|
|
26
|
+
taskSupport: 'required',
|
|
26
27
|
gotchas: [
|
|
27
28
|
'Literal mode is default; `isRegex=true` enables RE2 + capture replacements (`$1`, `$2`).',
|
|
28
29
|
],
|
|
@@ -38,6 +38,7 @@ export const SEARCH_FILES_TOOL = {
|
|
|
38
38
|
'Respects `.gitignore` unless `includeIgnored=true`.',
|
|
39
39
|
'Returns relative paths plus metadata; may truncate.',
|
|
40
40
|
],
|
|
41
|
+
taskSupport: 'required',
|
|
41
42
|
};
|
|
42
43
|
async function handleSearchFiles(args, signal, onProgress) {
|
|
43
44
|
const basePath = resolvePathOrRoot(args.path);
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -48,7 +48,6 @@ interface ToolErrorResponse extends Record<string, unknown> {
|
|
|
48
48
|
isError: true;
|
|
49
49
|
}
|
|
50
50
|
export type ToolResult<T> = ToolResponse<T> | ToolErrorResponse;
|
|
51
|
-
export declare function parseToolArgs<Schema extends z.ZodType>(schema: Schema, args: unknown): z.infer<Schema>;
|
|
52
51
|
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
52
|
type ProgressToken = string | number;
|
|
54
53
|
export interface ToolExtra {
|
package/dist/tools/shared.js
CHANGED
|
@@ -107,7 +107,7 @@ function resolveDetailedError(error, defaultCode, path) {
|
|
|
107
107
|
export function buildToolResponse(text, structuredContent, extraContent = []) {
|
|
108
108
|
return buildContentBlock(text, structuredContent, extraContent);
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
function parseToolArgs(schema, args) {
|
|
111
111
|
const candidate = args === undefined ? {} : args;
|
|
112
112
|
const parsed = schema.safeParse(candidate);
|
|
113
113
|
if (parsed.success) {
|
|
@@ -125,6 +125,11 @@ function canSendProgress(extra) {
|
|
|
125
125
|
return (extra._meta?.progressToken !== undefined &&
|
|
126
126
|
extra.sendNotification !== undefined);
|
|
127
127
|
}
|
|
128
|
+
function canReportProgress(extra) {
|
|
129
|
+
const taskExtra = extra;
|
|
130
|
+
const hasTask = taskExtra.taskId !== undefined && taskExtra.taskStore !== undefined;
|
|
131
|
+
return canSendProgress(extra) || hasTask;
|
|
132
|
+
}
|
|
128
133
|
export function withDefaultIcons(tool, iconInfo) {
|
|
129
134
|
if (!iconInfo) {
|
|
130
135
|
return maybeStripOutputSchema(tool);
|
|
@@ -216,25 +221,53 @@ export function buildToolErrorResponse(error, defaultCode, path) {
|
|
|
216
221
|
function buildNotInitializedResult() {
|
|
217
222
|
return buildToolErrorResponse(NOT_INITIALIZED_ERROR, ErrorCode.E_INVALID_INPUT);
|
|
218
223
|
}
|
|
219
|
-
async function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
async function reportProgress(extra, progress) {
|
|
225
|
+
const taskExtra = extra;
|
|
226
|
+
if (typeof taskExtra.taskId === 'string' &&
|
|
227
|
+
taskExtra.taskStore !== undefined &&
|
|
228
|
+
taskExtra.taskStore !== null) {
|
|
229
|
+
const store = taskExtra.taskStore;
|
|
230
|
+
if (typeof store.updateTaskStatus === 'function') {
|
|
231
|
+
try {
|
|
232
|
+
let statusMessage = progress.message;
|
|
233
|
+
if (progress.total !== undefined) {
|
|
234
|
+
statusMessage = statusMessage
|
|
235
|
+
? `${statusMessage} (${progress.current}/${progress.total})`
|
|
236
|
+
: `${progress.current}/${progress.total}`;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
statusMessage ??= `${progress.current}`;
|
|
240
|
+
}
|
|
241
|
+
await store.updateTaskStatus(taskExtra.taskId, 'working', statusMessage);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
console.error('Failed to update task status message:', error);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
227
247
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
248
|
+
if (canSendProgress(extra)) {
|
|
249
|
+
try {
|
|
250
|
+
await extra.sendNotification({
|
|
251
|
+
method: 'notifications/progress',
|
|
252
|
+
params: {
|
|
253
|
+
progressToken: extra._meta.progressToken,
|
|
254
|
+
progress: progress.current,
|
|
255
|
+
...(progress.total !== undefined ? { total: progress.total } : {}),
|
|
256
|
+
...(progress.message !== undefined
|
|
257
|
+
? { message: progress.message }
|
|
258
|
+
: {}),
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
console.error('Failed to send progress notification:', error);
|
|
264
|
+
}
|
|
231
265
|
}
|
|
232
266
|
}
|
|
233
267
|
export function createProgressReporter(extra) {
|
|
234
|
-
if (!
|
|
268
|
+
if (!canReportProgress(extra)) {
|
|
235
269
|
return () => { };
|
|
236
270
|
}
|
|
237
|
-
const token = extra._meta.progressToken;
|
|
238
271
|
// State for monotonic enforcement and rate-limiting.
|
|
239
272
|
let lastProgress = -1;
|
|
240
273
|
let lastSentMs = 0;
|
|
@@ -251,52 +284,41 @@ export function createProgressReporter(extra) {
|
|
|
251
284
|
return;
|
|
252
285
|
lastProgress = current;
|
|
253
286
|
lastSentMs = now;
|
|
254
|
-
void
|
|
255
|
-
|
|
256
|
-
progress: current,
|
|
287
|
+
void reportProgress(extra, {
|
|
288
|
+
current,
|
|
257
289
|
...(total !== undefined ? { total } : {}),
|
|
258
290
|
...(message !== undefined ? { message } : {}),
|
|
259
291
|
});
|
|
260
292
|
};
|
|
261
293
|
}
|
|
262
294
|
export function notifyProgress(extra, progress) {
|
|
263
|
-
if (!
|
|
295
|
+
if (!canReportProgress(extra))
|
|
264
296
|
return;
|
|
265
|
-
|
|
266
|
-
void sendProgressNotification(extra, {
|
|
267
|
-
progressToken: token,
|
|
268
|
-
progress: progress.current,
|
|
269
|
-
...(progress.total !== undefined ? { total: progress.total } : {}),
|
|
270
|
-
...(progress.message !== undefined ? { message: progress.message } : {}),
|
|
271
|
-
});
|
|
297
|
+
void reportProgress(extra, progress);
|
|
272
298
|
}
|
|
273
299
|
async function withProgress(message, extra, run, getCompletionMessage) {
|
|
274
|
-
if (!
|
|
300
|
+
if (!canReportProgress(extra)) {
|
|
275
301
|
return run();
|
|
276
302
|
}
|
|
277
|
-
const token = extra._meta.progressToken;
|
|
278
303
|
const total = 1;
|
|
279
|
-
await
|
|
280
|
-
|
|
281
|
-
progress: 0,
|
|
304
|
+
await reportProgress(extra, {
|
|
305
|
+
current: 0,
|
|
282
306
|
total,
|
|
283
307
|
message,
|
|
284
308
|
});
|
|
285
309
|
try {
|
|
286
310
|
const result = await run();
|
|
287
311
|
const endMessage = getCompletionMessage?.(result) ?? message;
|
|
288
|
-
await
|
|
289
|
-
|
|
290
|
-
progress: total,
|
|
312
|
+
await reportProgress(extra, {
|
|
313
|
+
current: total,
|
|
291
314
|
total,
|
|
292
315
|
message: endMessage,
|
|
293
316
|
});
|
|
294
317
|
return result;
|
|
295
318
|
}
|
|
296
319
|
catch (error) {
|
|
297
|
-
void
|
|
298
|
-
|
|
299
|
-
progress: total,
|
|
320
|
+
void reportProgress(extra, {
|
|
321
|
+
current: total,
|
|
300
322
|
total,
|
|
301
323
|
message: `${message} • failed`,
|
|
302
324
|
});
|
package/dist/tools/stat-many.js
CHANGED
|
@@ -13,6 +13,7 @@ export const GET_MULTIPLE_FILE_INFO_TOOL = {
|
|
|
13
13
|
inputSchema: GetMultipleFileInfoInputSchema,
|
|
14
14
|
outputSchema: GetMultipleFileInfoOutputSchema,
|
|
15
15
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
16
|
+
taskSupport: 'required',
|
|
16
17
|
nuances: ['Use before read/search when file size/type uncertainty exists.'],
|
|
17
18
|
};
|
|
18
19
|
function formatFileInfoDetail(info) {
|
|
@@ -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,13 +298,18 @@ 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);
|
|
305
305
|
if (!tasks?.registerToolTask)
|
|
306
306
|
return false;
|
|
307
|
-
|
|
307
|
+
const def = toolDef;
|
|
308
|
+
const existingExecution = def.execution ?? {};
|
|
309
|
+
const taskSupport = def.taskSupport ??
|
|
310
|
+
existingExecution.taskSupport ??
|
|
311
|
+
'optional';
|
|
312
|
+
tasks.registerToolTask(toolName, withDefaultIcons({ ...toolDef, execution: { ...existingExecution, taskSupport } }, iconInfo), taskHandler);
|
|
308
313
|
return true;
|
|
309
314
|
}
|
|
310
315
|
export function registerToolTaskIfAvailable(server, toolName, toolDef, run, iconInfo, guard) {
|
package/dist/tools/tree.js
CHANGED
|
@@ -14,6 +14,7 @@ export const TREE_TOOL = {
|
|
|
14
14
|
inputSchema: TreeInputSchema,
|
|
15
15
|
outputSchema: TreeOutputSchema,
|
|
16
16
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
17
|
+
taskSupport: 'required',
|
|
17
18
|
gotchas: ['`maxDepth=0` returns only the root node.'],
|
|
18
19
|
};
|
|
19
20
|
async function handleTree(args, signal, onProgress) {
|