@j0hanz/filesystem-mcp 1.16.2 → 1.16.3
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 +18 -14
- package/dist/assets/logo.svg +26 -11
- package/dist/cli.js +1 -1
- package/dist/completions.d.ts +1 -1
- package/dist/completions.js +1 -2
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.js +2 -3
- package/dist/lib/fs-helpers.d.ts +1 -1
- package/dist/lib/fs-helpers.js +0 -1
- package/dist/lib/logger.d.ts +3 -4
- package/dist/lib/logger.js +1 -1
- package/dist/lib/paths.d.ts +1 -1
- package/dist/prompts.d.ts +1 -1
- package/dist/prompts.js +11 -11
- package/dist/resources/workflows.js +12 -0
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +1 -1
- package/dist/schemas.d.ts +4 -4
- package/dist/server/bootstrap.d.ts +1 -10
- package/dist/server/bootstrap.js +165 -95
- package/dist/server/roots-manager.d.ts +3 -2
- package/dist/server/roots-manager.js +30 -4
- package/dist/server/task-store.d.ts +2 -3
- package/dist/server/task-store.js +1 -1
- package/dist/tools/apply-patch.d.ts +1 -1
- package/dist/tools/apply-patch.js +16 -12
- package/dist/tools/calculate-hash.d.ts +1 -1
- package/dist/tools/calculate-hash.js +8 -12
- package/dist/tools/contract.d.ts +5 -0
- package/dist/tools/create-directory.d.ts +1 -1
- package/dist/tools/create-directory.js +7 -10
- package/dist/tools/delete-file.d.ts +1 -1
- package/dist/tools/delete-file.js +12 -11
- package/dist/tools/diff-files.d.ts +1 -1
- package/dist/tools/diff-files.js +8 -11
- package/dist/tools/edit-file.d.ts +1 -1
- package/dist/tools/edit-file.js +12 -11
- package/dist/tools/icons.d.ts +15 -0
- package/dist/tools/icons.js +24 -0
- package/dist/tools/list-directory.d.ts +1 -1
- package/dist/tools/list-directory.js +7 -10
- package/dist/tools/move-file.d.ts +1 -1
- package/dist/tools/move-file.js +12 -11
- package/dist/tools/read-multiple.d.ts +1 -1
- package/dist/tools/read-multiple.js +8 -12
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.js +7 -10
- package/dist/tools/replace-in-files.d.ts +1 -1
- package/dist/tools/replace-in-files.js +11 -13
- package/dist/tools/roots.d.ts +1 -1
- package/dist/tools/roots.js +7 -10
- package/dist/tools/search-content.d.ts +1 -1
- package/dist/tools/search-content.js +8 -13
- package/dist/tools/search-files.d.ts +1 -1
- package/dist/tools/search-files.js +11 -16
- package/dist/tools/shared.d.ts +15 -12
- package/dist/tools/shared.js +84 -56
- package/dist/tools/stat-many.d.ts +1 -1
- package/dist/tools/stat-many.js +8 -12
- package/dist/tools/stat.d.ts +1 -1
- package/dist/tools/stat.js +7 -10
- package/dist/tools/task-support.d.ts +14 -12
- package/dist/tools/task-support.js +98 -92
- package/dist/tools/tree.d.ts +1 -1
- package/dist/tools/tree.js +11 -15
- package/dist/tools/write-file.d.ts +1 -1
- package/dist/tools/write-file.js +12 -11
- package/dist/tools.d.ts +1 -1
- package/package.json +6 -4
package/dist/tools/roots.js
CHANGED
|
@@ -2,8 +2,9 @@ import { ErrorCode } from '../lib/errors.js';
|
|
|
2
2
|
import { getAllowedDirectories } from '../lib/paths.js';
|
|
3
3
|
import { joinLines } from '../config.js';
|
|
4
4
|
import { ListAllowedDirectoriesInputSchema, ListAllowedDirectoriesOutputSchema, } from '../schemas.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { DIRECTORY_ICONS } from './icons.js';
|
|
6
|
+
import { buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, } from './shared.js';
|
|
7
|
+
import { registerStandardTool } from './task-support.js';
|
|
7
8
|
export const LIST_ALLOWED_DIRECTORIES_TOOL = {
|
|
8
9
|
name: 'roots',
|
|
9
10
|
title: 'Workspace Roots',
|
|
@@ -11,6 +12,7 @@ export const LIST_ALLOWED_DIRECTORIES_TOOL = {
|
|
|
11
12
|
inputSchema: ListAllowedDirectoriesInputSchema,
|
|
12
13
|
outputSchema: ListAllowedDirectoriesOutputSchema,
|
|
13
14
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
15
|
+
icons: DIRECTORY_ICONS,
|
|
14
16
|
taskSupport: 'forbidden',
|
|
15
17
|
};
|
|
16
18
|
function buildTextRoots(dirs) {
|
|
@@ -31,15 +33,14 @@ function handleListAllowedDirectories() {
|
|
|
31
33
|
return buildToolResponse(buildTextRoots(dirs), structured);
|
|
32
34
|
}
|
|
33
35
|
export function registerListAllowedDirectoriesTool(server, options = {}) {
|
|
34
|
-
const handler = (_args,
|
|
36
|
+
const handler = (_args, ctx) => executeToolWithDiagnostics({
|
|
35
37
|
toolName: 'roots',
|
|
36
|
-
|
|
38
|
+
ctx,
|
|
37
39
|
outputSchema: ListAllowedDirectoriesOutputSchema,
|
|
38
40
|
run: () => handleListAllowedDirectories(),
|
|
39
41
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.UNKNOWN),
|
|
40
42
|
});
|
|
41
|
-
|
|
42
|
-
guard: options.isInitialized,
|
|
43
|
+
registerStandardTool(server, LIST_ALLOWED_DIRECTORIES_TOOL, handler, options, {
|
|
43
44
|
progressMessage: () => '≣ roots',
|
|
44
45
|
completionMessage: (_args, result) => {
|
|
45
46
|
if (result.isError)
|
|
@@ -49,8 +50,4 @@ export function registerListAllowedDirectoriesTool(server, options = {}) {
|
|
|
49
50
|
return `≣ roots • ${count} ${count === 1 ? 'root' : 'roots'}`;
|
|
50
51
|
},
|
|
51
52
|
});
|
|
52
|
-
const validatedHandler = withValidatedArgs(ListAllowedDirectoriesInputSchema, wrappedHandler);
|
|
53
|
-
if (registerToolTaskIfAvailable(server, 'roots', LIST_ALLOWED_DIRECTORIES_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
54
|
-
return;
|
|
55
|
-
server.registerTool('roots', withDefaultIcons({ ...LIST_ALLOWED_DIRECTORIES_TOOL }, options.iconInfo), validatedHandler);
|
|
56
53
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
3
|
export declare const SEARCH_CONTENT_TOOL: ToolContract;
|
|
4
4
|
export declare function registerSearchContentTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -5,8 +5,9 @@ import { ErrorCode, formatUnknownErrorMessage, McpError, } from '../lib/errors.j
|
|
|
5
5
|
import { searchContent, } from '../lib/file-operations/search.js';
|
|
6
6
|
import { formatOperationSummary } from '../config.js';
|
|
7
7
|
import { SearchContentInputSchema, SearchContentOutputSchema, } from '../schemas.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { SEARCH_ICONS } from './icons.js';
|
|
9
|
+
import { buildResourceLink, buildToolErrorResponse, buildToolResponse, createToolProgressSession, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, resolveFinalProgressCurrent, resolvePathOrRoot, truncateProgressPattern, } from './shared.js';
|
|
10
|
+
import { registerStandardTool } from './task-support.js';
|
|
10
11
|
/**
|
|
11
12
|
* Configuration constants for the Search Content tool.
|
|
12
13
|
*/
|
|
@@ -70,6 +71,7 @@ export const SEARCH_CONTENT_TOOL = {
|
|
|
70
71
|
inputSchema: SearchContentInputSchema,
|
|
71
72
|
outputSchema: SearchContentOutputSchema,
|
|
72
73
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
74
|
+
icons: SEARCH_ICONS,
|
|
73
75
|
nuances: [
|
|
74
76
|
'Inline results capped at 50 matches; full results via `resourceUri`.',
|
|
75
77
|
],
|
|
@@ -259,15 +261,15 @@ async function handleSearchContent(args, signal, resourceStore, onProgress) {
|
|
|
259
261
|
return buildToolResponse(text, fullStructured);
|
|
260
262
|
}
|
|
261
263
|
export function registerSearchContentTool(server, options = {}) {
|
|
262
|
-
const handler = (args,
|
|
264
|
+
const handler = (args, ctx) => executeToolWithDiagnostics({
|
|
263
265
|
toolName: 'grep',
|
|
264
|
-
|
|
266
|
+
ctx,
|
|
265
267
|
outputSchema: SearchContentOutputSchema,
|
|
266
268
|
context: { path: args.path ?? '.' },
|
|
267
269
|
run: async (signal) => {
|
|
268
270
|
const { pattern, filePattern: scope } = args;
|
|
269
271
|
const progressLabel = `🔎︎ grep: ${truncateProgressPattern(pattern)}`;
|
|
270
|
-
const progress = createToolProgressSession(
|
|
272
|
+
const progress = createToolProgressSession(ctx, progressLabel);
|
|
271
273
|
const progressWithMessage = ({ current, total, }) => {
|
|
272
274
|
progress.update({
|
|
273
275
|
current,
|
|
@@ -291,12 +293,5 @@ export function registerSearchContentTool(server, options = {}) {
|
|
|
291
293
|
},
|
|
292
294
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.UNKNOWN, args.path ?? '.'),
|
|
293
295
|
});
|
|
294
|
-
|
|
295
|
-
const wrappedHandler = wrapToolHandler(handler, {
|
|
296
|
-
guard: isInitialized,
|
|
297
|
-
});
|
|
298
|
-
const validatedHandler = withValidatedArgs(SearchContentInputSchema, wrappedHandler);
|
|
299
|
-
if (registerToolTaskIfAvailable(server, 'grep', SEARCH_CONTENT_TOOL, validatedHandler, options.iconInfo, isInitialized))
|
|
300
|
-
return;
|
|
301
|
-
server.registerTool('grep', withDefaultIcons({ ...SEARCH_CONTENT_TOOL }, options.iconInfo), validatedHandler);
|
|
296
|
+
registerStandardTool(server, SEARCH_CONTENT_TOOL, handler, options);
|
|
302
297
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
3
|
export declare const SEARCH_FILES_TOOL: ToolContract;
|
|
4
4
|
export declare function registerSearchFilesTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
@@ -4,8 +4,9 @@ import { ErrorCode } from '../lib/errors.js';
|
|
|
4
4
|
import { searchFiles } from '../lib/file-operations/search.js';
|
|
5
5
|
import { formatOperationSummary, joinLines } from '../config.js';
|
|
6
6
|
import { SearchFilesInputSchema, SearchFilesOutputSchema } from '../schemas.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { DIRECTORY_ICONS } from './icons.js';
|
|
8
|
+
import { buildToolErrorResponse, buildToolResponse, createProgressReporter, decodeOffsetCursor, encodeOffsetCursor, executeToolWithDiagnostics, notifyProgress, READ_ONLY_TOOL_ANNOTATIONS, resolvePathOrRoot, truncateProgressPattern, } from './shared.js';
|
|
9
|
+
import { registerStandardTool } from './task-support.js';
|
|
9
10
|
export const SEARCH_FILES_TOOL = {
|
|
10
11
|
name: 'find',
|
|
11
12
|
title: 'Find Files',
|
|
@@ -14,6 +15,7 @@ export const SEARCH_FILES_TOOL = {
|
|
|
14
15
|
inputSchema: SearchFilesInputSchema,
|
|
15
16
|
outputSchema: SearchFilesOutputSchema,
|
|
16
17
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
18
|
+
icons: DIRECTORY_ICONS,
|
|
17
19
|
nuances: ['Respects `.gitignore` unless `includeIgnored=true`.'],
|
|
18
20
|
taskSupport: 'optional',
|
|
19
21
|
};
|
|
@@ -109,9 +111,9 @@ async function handleSearchFiles(args, signal, onProgress) {
|
|
|
109
111
|
return buildToolResponse(text, structured);
|
|
110
112
|
}
|
|
111
113
|
export function registerSearchFilesTool(server, options = {}) {
|
|
112
|
-
const handler = (args,
|
|
114
|
+
const handler = (args, ctx) => executeToolWithDiagnostics({
|
|
113
115
|
toolName: 'find',
|
|
114
|
-
|
|
116
|
+
ctx,
|
|
115
117
|
outputSchema: SearchFilesOutputSchema,
|
|
116
118
|
timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
|
|
117
119
|
context: { path: args.path ?? '.' },
|
|
@@ -122,11 +124,11 @@ export function registerSearchFilesTool(server, options = {}) {
|
|
|
122
124
|
const truncatedPattern = truncateProgressPattern(pattern);
|
|
123
125
|
const context = `${truncatedPattern} in ${scopeLabel}`;
|
|
124
126
|
let progressCursor = 0;
|
|
125
|
-
notifyProgress(
|
|
127
|
+
notifyProgress(ctx, {
|
|
126
128
|
current: 0,
|
|
127
129
|
message: `🔎︎ find: ${truncatedPattern}`,
|
|
128
130
|
});
|
|
129
|
-
const baseReporter = createProgressReporter(
|
|
131
|
+
const baseReporter = createProgressReporter(ctx);
|
|
130
132
|
const progressWithMessage = ({ current, total, }) => {
|
|
131
133
|
if (current > progressCursor)
|
|
132
134
|
progressCursor = current;
|
|
@@ -157,7 +159,7 @@ export function registerSearchFilesTool(server, options = {}) {
|
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
const finalCurrent = Math.max((sc.filesScanned ?? 0) + 1, progressCursor + 1);
|
|
160
|
-
notifyProgress(
|
|
162
|
+
notifyProgress(ctx, {
|
|
161
163
|
current: finalCurrent,
|
|
162
164
|
total: finalCurrent,
|
|
163
165
|
message: `🔎︎ find: ${context} • ${suffix}`,
|
|
@@ -166,7 +168,7 @@ export function registerSearchFilesTool(server, options = {}) {
|
|
|
166
168
|
}
|
|
167
169
|
catch (error) {
|
|
168
170
|
const finalCurrent = Math.max(progressCursor + 1, 1);
|
|
169
|
-
notifyProgress(
|
|
171
|
+
notifyProgress(ctx, {
|
|
170
172
|
current: finalCurrent,
|
|
171
173
|
total: finalCurrent,
|
|
172
174
|
message: `🔎︎ find: ${context} • failed`,
|
|
@@ -176,12 +178,5 @@ export function registerSearchFilesTool(server, options = {}) {
|
|
|
176
178
|
},
|
|
177
179
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.UNKNOWN, args.path),
|
|
178
180
|
});
|
|
179
|
-
|
|
180
|
-
const wrappedHandler = wrapToolHandler(handler, {
|
|
181
|
-
guard: isInitialized,
|
|
182
|
-
});
|
|
183
|
-
const validatedHandler = withValidatedArgs(SearchFilesInputSchema, wrappedHandler);
|
|
184
|
-
if (registerToolTaskIfAvailable(server, 'find', SEARCH_FILES_TOOL, validatedHandler, options.iconInfo, isInitialized))
|
|
185
|
-
return;
|
|
186
|
-
server.registerTool('find', withDefaultIcons({ ...SEARCH_FILES_TOOL }, options.iconInfo), validatedHandler);
|
|
181
|
+
registerStandardTool(server, SEARCH_FILES_TOOL, handler, options);
|
|
187
182
|
}
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContentBlock, Icon, ProgressNotificationParams } from '@modelcontextprotocol/
|
|
1
|
+
import type { ContentBlock, Icon, LoggingLevel, ProgressNotificationParams, ServerContext } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ErrorCode } from '../lib/errors.js';
|
|
4
4
|
import type { ResourceStore } from '../lib/resource-store.js';
|
|
@@ -22,6 +22,8 @@ export declare const IDEMPOTENT_WRITE_TOOL_ANNOTATIONS: {
|
|
|
22
22
|
readonly destructiveHint: false;
|
|
23
23
|
readonly openWorldHint: false;
|
|
24
24
|
};
|
|
25
|
+
export type TaskSupportLevel = 'optional' | 'required' | 'forbidden';
|
|
26
|
+
export declare function resolveToolTaskSupportLevel(topLevelTaskSupport: unknown, executionTaskSupport: unknown): TaskSupportLevel | undefined;
|
|
25
27
|
export declare function maybeStripStructuredContentFromResult<T extends object>(result: T): T;
|
|
26
28
|
type ResourceEntry = ReturnType<ResourceStore['putText']>;
|
|
27
29
|
export declare function maybeExternalizeTextContent(resourceStore: ResourceStore | undefined, content: string, params: {
|
|
@@ -54,23 +56,24 @@ export type ToolResponse<T> = ReturnType<typeof buildToolResponse<T>> & {
|
|
|
54
56
|
interface ToolErrorResponse extends Record<string, unknown> {
|
|
55
57
|
content: ContentBlock[];
|
|
56
58
|
isError: true;
|
|
57
|
-
errorCode?:
|
|
59
|
+
errorCode?: ErrorCode;
|
|
58
60
|
}
|
|
59
61
|
export type ToolResult<T> = ToolResponse<T> | ToolErrorResponse;
|
|
60
|
-
export declare function withValidatedArgs<Args, Result>(schema: z.ZodType<Args>, handler: (args: Args,
|
|
62
|
+
export declare function withValidatedArgs<Args, Result>(schema: z.ZodType<Args>, handler: (args: Args, ctx: ToolContext) => Promise<ToolResult<Result>>): (args: unknown, ctx: ToolContext | ServerContext) => Promise<ToolResult<Result>>;
|
|
61
63
|
type ProgressToken = string | number;
|
|
62
|
-
export interface
|
|
64
|
+
export interface ToolContext {
|
|
63
65
|
signal?: AbortSignal;
|
|
64
66
|
_meta?: {
|
|
65
67
|
progressToken?: ProgressToken | undefined;
|
|
66
68
|
traceparent?: string | undefined;
|
|
67
69
|
tracestate?: string | undefined;
|
|
68
70
|
baggage?: string | undefined;
|
|
69
|
-
};
|
|
71
|
+
} | undefined;
|
|
70
72
|
sendNotification?: (notification: {
|
|
71
73
|
method: 'notifications/progress';
|
|
72
74
|
params: ProgressNotificationParams;
|
|
73
75
|
}) => Promise<void>;
|
|
76
|
+
log?: (level: LoggingLevel, data: unknown, logger?: string) => Promise<void>;
|
|
74
77
|
}
|
|
75
78
|
export interface IconInfo {
|
|
76
79
|
src: string;
|
|
@@ -102,7 +105,7 @@ interface FileInfoPayload {
|
|
|
102
105
|
export declare function buildFileInfoPayload(info: FileInfo): FileInfoPayload;
|
|
103
106
|
interface ToolExecutionOptions<T> {
|
|
104
107
|
toolName: string;
|
|
105
|
-
|
|
108
|
+
ctx: ToolContext;
|
|
106
109
|
outputSchema?: z.ZodType<T>;
|
|
107
110
|
run: (signal: AbortSignal | undefined) => ToolResponse<T> | Promise<ToolResponse<T>>;
|
|
108
111
|
onError: (error: unknown) => ToolResult<T>;
|
|
@@ -113,12 +116,12 @@ interface ToolExecutionOptions<T> {
|
|
|
113
116
|
}
|
|
114
117
|
export declare function executeToolWithDiagnostics<T>(options: ToolExecutionOptions<T>): Promise<ToolResult<T>>;
|
|
115
118
|
export declare function buildToolErrorResponse(error: unknown, defaultCode: ErrorCode, path?: string): ToolErrorResponse;
|
|
116
|
-
export declare function createProgressReporter(
|
|
119
|
+
export declare function createProgressReporter(ctx: ToolContext): (progress: {
|
|
117
120
|
total?: number;
|
|
118
121
|
current: number;
|
|
119
122
|
message?: string;
|
|
120
123
|
}) => void;
|
|
121
|
-
export declare function notifyProgress(
|
|
124
|
+
export declare function notifyProgress(ctx: ToolContext, progress: {
|
|
122
125
|
current: number;
|
|
123
126
|
total?: number;
|
|
124
127
|
message?: string;
|
|
@@ -138,19 +141,19 @@ interface BatchProgressCallbacks {
|
|
|
138
141
|
progress: ToolProgressSession;
|
|
139
142
|
onItemComplete: () => void;
|
|
140
143
|
}
|
|
141
|
-
export declare function createToolProgressSession(
|
|
142
|
-
export declare function createBatchProgressCallbacks(
|
|
144
|
+
export declare function createToolProgressSession(ctx: ToolContext, startMessage: string, initialTotal?: number): ToolProgressSession;
|
|
145
|
+
export declare function createBatchProgressCallbacks(ctx: ToolContext, params: {
|
|
143
146
|
toolLabel: string;
|
|
144
147
|
context: string;
|
|
145
148
|
totalItems: number;
|
|
146
149
|
itemVerb: string;
|
|
147
150
|
}): BatchProgressCallbacks;
|
|
148
151
|
export declare function resolveFinalProgressCurrent(progress: ToolProgressSession, ...candidates: number[]): number;
|
|
149
|
-
export declare function wrapToolHandler<Args, Result>(handler: (args: Args,
|
|
152
|
+
export declare function wrapToolHandler<Args, Result>(handler: (args: Args, ctx: ToolContext) => Promise<ToolResult<Result>>, options: {
|
|
150
153
|
guard?: (() => boolean) | undefined;
|
|
151
154
|
progressMessage?: (args: Args) => string;
|
|
152
155
|
completionMessage?: (args: Args, result: ToolResult<Result>) => string | undefined;
|
|
153
|
-
}): (args: Args,
|
|
156
|
+
}): (args: Args, ctx?: ToolContext | ServerContext) => Promise<ToolResult<Result>>;
|
|
154
157
|
/**
|
|
155
158
|
* Returns `pathValue` if non-empty; otherwise resolves to the single allowed
|
|
156
159
|
* directory from module-level state managed by `RootsManager`. Throws when the
|
package/dist/tools/shared.js
CHANGED
|
@@ -53,32 +53,43 @@ export const IDEMPOTENT_WRITE_TOOL_ANNOTATIONS = {
|
|
|
53
53
|
function isTaskSupportLevel(value) {
|
|
54
54
|
return value === 'optional' || value === 'required' || value === 'forbidden';
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
? candidate['execution']
|
|
56
|
+
function getExecutionConfig(candidate) {
|
|
57
|
+
const { execution } = candidate;
|
|
58
|
+
return execution && typeof execution === 'object'
|
|
59
|
+
? execution
|
|
61
60
|
: undefined;
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
}
|
|
62
|
+
export function resolveToolTaskSupportLevel(topLevelTaskSupport, executionTaskSupport) {
|
|
64
63
|
if (isTaskSupportLevel(topLevelTaskSupport)) {
|
|
65
|
-
|
|
64
|
+
return topLevelTaskSupport;
|
|
65
|
+
}
|
|
66
|
+
if (isTaskSupportLevel(executionTaskSupport)) {
|
|
67
|
+
return executionTaskSupport;
|
|
66
68
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function buildNormalizedExecution(existingExecution, taskSupport) {
|
|
72
|
+
if (taskSupport === undefined && existingExecution === undefined) {
|
|
73
|
+
return undefined;
|
|
69
74
|
}
|
|
75
|
+
return {
|
|
76
|
+
...(existingExecution ?? {}),
|
|
77
|
+
...(taskSupport !== undefined ? { taskSupport } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function normalizeToolExecution(tool) {
|
|
81
|
+
const candidate = tool;
|
|
82
|
+
const topLevelTaskSupport = candidate['taskSupport'];
|
|
83
|
+
const existingExecution = getExecutionConfig(candidate);
|
|
84
|
+
const resolvedTaskSupport = resolveToolTaskSupportLevel(topLevelTaskSupport, existingExecution?.['taskSupport']);
|
|
70
85
|
if (resolvedTaskSupport === undefined && topLevelTaskSupport === undefined) {
|
|
71
86
|
return tool;
|
|
72
87
|
}
|
|
73
88
|
const normalized = { ...candidate };
|
|
74
89
|
delete normalized['taskSupport'];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
...(resolvedTaskSupport !== undefined
|
|
79
|
-
? { taskSupport: resolvedTaskSupport }
|
|
80
|
-
: {}),
|
|
81
|
-
};
|
|
90
|
+
const execution = buildNormalizedExecution(existingExecution, resolvedTaskSupport);
|
|
91
|
+
if (execution !== undefined) {
|
|
92
|
+
normalized['execution'] = execution;
|
|
82
93
|
}
|
|
83
94
|
return normalized;
|
|
84
95
|
}
|
|
@@ -199,10 +210,10 @@ function parseToolArgs(schema, args) {
|
|
|
199
210
|
throw new McpError(ErrorCode.INVALID_INPUT, `Invalid tool arguments:\n${z.prettifyError(parsed.error)}`);
|
|
200
211
|
}
|
|
201
212
|
export function withValidatedArgs(schema, handler) {
|
|
202
|
-
return async (args,
|
|
213
|
+
return async (args, ctx) => {
|
|
203
214
|
try {
|
|
204
215
|
const normalizedArgs = parseToolArgs(schema, args);
|
|
205
|
-
return await handler(normalizedArgs,
|
|
216
|
+
return await handler(normalizedArgs, toToolContext(ctx));
|
|
206
217
|
}
|
|
207
218
|
catch (error) {
|
|
208
219
|
if (error instanceof McpError && error.code === ErrorCode.INVALID_INPUT) {
|
|
@@ -212,14 +223,31 @@ export function withValidatedArgs(schema, handler) {
|
|
|
212
223
|
}
|
|
213
224
|
};
|
|
214
225
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
226
|
+
function toToolContext(ctx) {
|
|
227
|
+
if (!ctx)
|
|
228
|
+
return {};
|
|
229
|
+
if ('mcpReq' in ctx) {
|
|
230
|
+
return {
|
|
231
|
+
signal: ctx.mcpReq.signal,
|
|
232
|
+
...(ctx.mcpReq._meta
|
|
233
|
+
? { _meta: ctx.mcpReq._meta }
|
|
234
|
+
: {}),
|
|
235
|
+
sendNotification: async (notification) => ctx.mcpReq.notify(notification),
|
|
236
|
+
log: async (level, data, logger) => ctx.mcpReq.notify({
|
|
237
|
+
method: 'notifications/message',
|
|
238
|
+
params: { level, data, ...(logger ? { logger } : {}) },
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return ctx;
|
|
243
|
+
}
|
|
244
|
+
function canSendProgress(ctx) {
|
|
245
|
+
return (ctx._meta?.progressToken !== undefined && ctx.sendNotification !== undefined);
|
|
218
246
|
}
|
|
219
|
-
function canReportProgress(
|
|
220
|
-
const taskExtra =
|
|
247
|
+
function canReportProgress(ctx) {
|
|
248
|
+
const taskExtra = ctx;
|
|
221
249
|
const hasTask = taskExtra.taskId !== undefined && taskExtra.taskStore !== undefined;
|
|
222
|
-
return canSendProgress(
|
|
250
|
+
return canSendProgress(ctx) || hasTask;
|
|
223
251
|
}
|
|
224
252
|
export function withDefaultIcons(tool, iconInfo) {
|
|
225
253
|
const normalizedTool = normalizeToolExecution(tool);
|
|
@@ -278,9 +306,9 @@ function getToolSignal(extraSignal, timedSignal) {
|
|
|
278
306
|
return { signal, cleanup };
|
|
279
307
|
}
|
|
280
308
|
export async function executeToolWithDiagnostics(options) {
|
|
281
|
-
const traceContext = extractTraceContext(options.
|
|
309
|
+
const traceContext = extractTraceContext(options.ctx._meta);
|
|
282
310
|
return withToolDiagnostics(options.toolName, () => withToolErrorHandling(async () => {
|
|
283
|
-
const { signal, cleanup } = getToolSignal(options.
|
|
311
|
+
const { signal, cleanup } = getToolSignal(options.ctx.signal, options.timedSignal);
|
|
284
312
|
try {
|
|
285
313
|
return validateToolResponse(options.toolName, await options.run(signal), options.outputSchema);
|
|
286
314
|
}
|
|
@@ -304,9 +332,9 @@ export function buildToolErrorResponse(error, defaultCode, path) {
|
|
|
304
332
|
function buildNotInitializedResult() {
|
|
305
333
|
return buildToolErrorResponse(NOT_INITIALIZED_ERROR, ErrorCode.INVALID_INPUT);
|
|
306
334
|
}
|
|
307
|
-
async function reportProgress(
|
|
308
|
-
await updateTaskStoreProgress(
|
|
309
|
-
await sendMcpProgressNotification(
|
|
335
|
+
async function reportProgress(ctx, progress) {
|
|
336
|
+
await updateTaskStoreProgress(ctx, progress);
|
|
337
|
+
await sendMcpProgressNotification(ctx, progress);
|
|
310
338
|
}
|
|
311
339
|
function formatTaskStatusMessage(progress) {
|
|
312
340
|
if (progress.total !== undefined) {
|
|
@@ -320,8 +348,8 @@ function isBenignTaskStatusUpdateError(error) {
|
|
|
320
348
|
return (error instanceof Error &&
|
|
321
349
|
/Task .*not found|terminal status/iu.test(error.message));
|
|
322
350
|
}
|
|
323
|
-
async function updateTaskStoreProgress(
|
|
324
|
-
const taskExtra =
|
|
351
|
+
async function updateTaskStoreProgress(ctx, progress) {
|
|
352
|
+
const taskExtra = ctx;
|
|
325
353
|
if (typeof taskExtra.taskId === 'string' &&
|
|
326
354
|
taskExtra.taskStore !== undefined &&
|
|
327
355
|
taskExtra.taskStore !== null) {
|
|
@@ -338,13 +366,13 @@ async function updateTaskStoreProgress(extra, progress) {
|
|
|
338
366
|
}
|
|
339
367
|
}
|
|
340
368
|
}
|
|
341
|
-
async function sendMcpProgressNotification(
|
|
342
|
-
if (canSendProgress(
|
|
369
|
+
async function sendMcpProgressNotification(ctx, progress) {
|
|
370
|
+
if (canSendProgress(ctx)) {
|
|
343
371
|
try {
|
|
344
|
-
await
|
|
372
|
+
await ctx.sendNotification({
|
|
345
373
|
method: 'notifications/progress',
|
|
346
374
|
params: {
|
|
347
|
-
progressToken:
|
|
375
|
+
progressToken: ctx._meta.progressToken,
|
|
348
376
|
progress: progress.current,
|
|
349
377
|
...(progress.total !== undefined ? { total: progress.total } : {}),
|
|
350
378
|
...(progress.message !== undefined
|
|
@@ -358,8 +386,8 @@ async function sendMcpProgressNotification(extra, progress) {
|
|
|
358
386
|
}
|
|
359
387
|
}
|
|
360
388
|
}
|
|
361
|
-
export function createProgressReporter(
|
|
362
|
-
if (!canReportProgress(
|
|
389
|
+
export function createProgressReporter(ctx) {
|
|
390
|
+
if (!canReportProgress(ctx)) {
|
|
363
391
|
return () => { };
|
|
364
392
|
}
|
|
365
393
|
// State for monotonic enforcement and rate-limiting.
|
|
@@ -379,26 +407,26 @@ export function createProgressReporter(extra) {
|
|
|
379
407
|
return;
|
|
380
408
|
lastProgress = current;
|
|
381
409
|
lastSentMs = now;
|
|
382
|
-
void reportProgress(
|
|
410
|
+
void reportProgress(ctx, {
|
|
383
411
|
current,
|
|
384
412
|
...(total !== undefined ? { total } : {}),
|
|
385
413
|
...(message !== undefined ? { message } : {}),
|
|
386
414
|
});
|
|
387
415
|
};
|
|
388
416
|
}
|
|
389
|
-
export function notifyProgress(
|
|
390
|
-
if (!canReportProgress(
|
|
417
|
+
export function notifyProgress(ctx, progress) {
|
|
418
|
+
if (!canReportProgress(ctx))
|
|
391
419
|
return;
|
|
392
|
-
void reportProgress(
|
|
420
|
+
void reportProgress(ctx, progress);
|
|
393
421
|
}
|
|
394
|
-
export function createToolProgressSession(
|
|
395
|
-
notifyProgress(
|
|
422
|
+
export function createToolProgressSession(ctx, startMessage, initialTotal) {
|
|
423
|
+
notifyProgress(ctx, {
|
|
396
424
|
current: 0,
|
|
397
425
|
...(initialTotal !== undefined ? { total: initialTotal } : {}),
|
|
398
426
|
message: startMessage,
|
|
399
427
|
});
|
|
400
428
|
let cursor = 0;
|
|
401
|
-
const baseReporter = createProgressReporter(
|
|
429
|
+
const baseReporter = createProgressReporter(ctx);
|
|
402
430
|
const setCursor = (value) => {
|
|
403
431
|
if (value > cursor)
|
|
404
432
|
cursor = value;
|
|
@@ -406,7 +434,7 @@ export function createToolProgressSession(extra, startMessage, initialTotal) {
|
|
|
406
434
|
};
|
|
407
435
|
const finishProgress = (message, minimumCurrent) => {
|
|
408
436
|
const finalCurrent = Math.max(cursor + 1, minimumCurrent ?? 1, 1);
|
|
409
|
-
notifyProgress(
|
|
437
|
+
notifyProgress(ctx, {
|
|
410
438
|
current: finalCurrent,
|
|
411
439
|
total: finalCurrent,
|
|
412
440
|
...(message !== undefined ? { message } : {}),
|
|
@@ -434,8 +462,8 @@ export function createToolProgressSession(extra, startMessage, initialTotal) {
|
|
|
434
462
|
getCurrent: () => cursor,
|
|
435
463
|
};
|
|
436
464
|
}
|
|
437
|
-
export function createBatchProgressCallbacks(
|
|
438
|
-
const progress = createToolProgressSession(
|
|
465
|
+
export function createBatchProgressCallbacks(ctx, params) {
|
|
466
|
+
const progress = createToolProgressSession(ctx, `${params.toolLabel}: ${params.context}`, params.totalItems);
|
|
439
467
|
let itemsDone = 0;
|
|
440
468
|
const onItemComplete = () => {
|
|
441
469
|
itemsDone++;
|
|
@@ -456,21 +484,21 @@ export function resolveFinalProgressCurrent(progress, ...candidates) {
|
|
|
456
484
|
}
|
|
457
485
|
return finalCurrent;
|
|
458
486
|
}
|
|
459
|
-
async function withProgress(message,
|
|
460
|
-
if (!canReportProgress(
|
|
487
|
+
async function withProgress(message, ctx, run, getCompletionMessage) {
|
|
488
|
+
if (!canReportProgress(ctx)) {
|
|
461
489
|
return run();
|
|
462
490
|
}
|
|
463
491
|
const total = 1;
|
|
464
492
|
// Emit the start notification only when a progressToken is present; for
|
|
465
493
|
// task-only mode the task status is already 'working' — a zero-progress
|
|
466
494
|
// notification would add unnecessary overhead without client value.
|
|
467
|
-
if (canSendProgress(
|
|
468
|
-
await reportProgress(
|
|
495
|
+
if (canSendProgress(ctx)) {
|
|
496
|
+
await reportProgress(ctx, { current: 0, total, message });
|
|
469
497
|
}
|
|
470
498
|
try {
|
|
471
499
|
const result = await run();
|
|
472
500
|
const endMessage = getCompletionMessage?.(result) ?? message;
|
|
473
|
-
await reportProgress(
|
|
501
|
+
await reportProgress(ctx, {
|
|
474
502
|
current: total,
|
|
475
503
|
total,
|
|
476
504
|
message: endMessage,
|
|
@@ -478,7 +506,7 @@ async function withProgress(message, extra, run, getCompletionMessage) {
|
|
|
478
506
|
return result;
|
|
479
507
|
}
|
|
480
508
|
catch (error) {
|
|
481
|
-
void reportProgress(
|
|
509
|
+
void reportProgress(ctx, {
|
|
482
510
|
current: total,
|
|
483
511
|
total,
|
|
484
512
|
message: `${message} • failed`,
|
|
@@ -487,8 +515,8 @@ async function withProgress(message, extra, run, getCompletionMessage) {
|
|
|
487
515
|
}
|
|
488
516
|
}
|
|
489
517
|
export function wrapToolHandler(handler, options) {
|
|
490
|
-
return async (args,
|
|
491
|
-
const resolvedExtra =
|
|
518
|
+
return async (args, ctx) => {
|
|
519
|
+
const resolvedExtra = toToolContext(ctx);
|
|
492
520
|
if (options.guard && !options.guard()) {
|
|
493
521
|
return maybeStripStructuredContentFromResult(buildNotInitializedResult());
|
|
494
522
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
3
|
export declare const GET_MULTIPLE_FILE_INFO_TOOL: ToolContract;
|
|
4
4
|
export declare function registerGetMultipleFileInfoTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
package/dist/tools/stat-many.js
CHANGED
|
@@ -3,8 +3,9 @@ import { ErrorCode } from '../lib/errors.js';
|
|
|
3
3
|
import { getMultipleFileInfo } from '../lib/file-operations/metadata.js';
|
|
4
4
|
import { formatBytes, joinLines } from '../config.js';
|
|
5
5
|
import { GetMultipleFileInfoInputSchema, GetMultipleFileInfoOutputSchema, } from '../schemas.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { FILE_READ_ICONS } from './icons.js';
|
|
7
|
+
import { buildBatchCompletionSuffix, buildBatchPathContext, buildFileInfoPayload, buildStructuredError, buildToolErrorResponse, buildToolResponse, createBatchProgressCallbacks, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, resolveFinalProgressCurrent, } from './shared.js';
|
|
8
|
+
import { registerStandardTool } from './task-support.js';
|
|
8
9
|
export const GET_MULTIPLE_FILE_INFO_TOOL = {
|
|
9
10
|
name: 'stat_many',
|
|
10
11
|
title: 'Get Multiple File Info',
|
|
@@ -13,6 +14,7 @@ export const GET_MULTIPLE_FILE_INFO_TOOL = {
|
|
|
13
14
|
inputSchema: GetMultipleFileInfoInputSchema,
|
|
14
15
|
outputSchema: GetMultipleFileInfoOutputSchema,
|
|
15
16
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
17
|
+
icons: FILE_READ_ICONS,
|
|
16
18
|
taskSupport: 'optional',
|
|
17
19
|
};
|
|
18
20
|
function formatFileInfoDetail(info) {
|
|
@@ -64,17 +66,17 @@ async function handleGetMultipleFileInfo(args, signal, onProgress) {
|
|
|
64
66
|
return buildToolResponse(text, structured);
|
|
65
67
|
}
|
|
66
68
|
export function registerGetMultipleFileInfoTool(server, options = {}) {
|
|
67
|
-
const handler = (args,
|
|
69
|
+
const handler = (args, ctx) => {
|
|
68
70
|
const primaryPath = args.paths[0] ?? '';
|
|
69
71
|
return executeToolWithDiagnostics({
|
|
70
72
|
toolName: 'stat_many',
|
|
71
|
-
|
|
73
|
+
ctx,
|
|
72
74
|
outputSchema: GetMultipleFileInfoOutputSchema,
|
|
73
75
|
timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
|
|
74
76
|
context: { path: primaryPath },
|
|
75
77
|
run: async (signal) => {
|
|
76
78
|
const context = buildBatchPathContext(args.paths);
|
|
77
|
-
const { progress, onItemComplete } = createBatchProgressCallbacks(
|
|
79
|
+
const { progress, onItemComplete } = createBatchProgressCallbacks(ctx, {
|
|
78
80
|
toolLabel: '🕮 stat_many',
|
|
79
81
|
context,
|
|
80
82
|
totalItems: args.paths.length,
|
|
@@ -97,11 +99,5 @@ export function registerGetMultipleFileInfoTool(server, options = {}) {
|
|
|
97
99
|
onError: (error) => buildToolErrorResponse(error, ErrorCode.NOT_FOUND, primaryPath),
|
|
98
100
|
});
|
|
99
101
|
};
|
|
100
|
-
|
|
101
|
-
guard: options.isInitialized,
|
|
102
|
-
});
|
|
103
|
-
const validatedHandler = withValidatedArgs(GetMultipleFileInfoInputSchema, wrappedHandler);
|
|
104
|
-
if (registerToolTaskIfAvailable(server, 'stat_many', GET_MULTIPLE_FILE_INFO_TOOL, validatedHandler, options.iconInfo, options.isInitialized))
|
|
105
|
-
return;
|
|
106
|
-
server.registerTool('stat_many', withDefaultIcons({ ...GET_MULTIPLE_FILE_INFO_TOOL }, options.iconInfo), validatedHandler);
|
|
102
|
+
registerStandardTool(server, GET_MULTIPLE_FILE_INFO_TOOL, handler, options);
|
|
107
103
|
}
|
package/dist/tools/stat.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { type ToolContract, type ToolRegistrationOptions } from './shared.js';
|
|
3
3
|
export declare const GET_FILE_INFO_TOOL: ToolContract;
|
|
4
4
|
export declare function registerGetFileInfoTool(server: McpServer, options?: ToolRegistrationOptions): void;
|