@j0hanz/filesystem-mcp 2.6.3 → 2.7.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/README.md +27 -18
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +5 -2
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +22 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/concurrency.d.ts +0 -17
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +0 -10
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/config.d.ts +7 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +7 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/diff.d.ts +0 -4
- package/dist/core/diff.d.ts.map +1 -1
- package/dist/core/diff.js +1 -15
- package/dist/core/diff.js.map +1 -1
- package/dist/core/file-uri.d.ts +9 -9
- package/dist/core/file-uri.d.ts.map +1 -1
- package/dist/core/file-uri.js +16 -11
- package/dist/core/file-uri.js.map +1 -1
- package/dist/core/fs.d.ts +1 -1
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +65 -56
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +6 -0
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +64 -8
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +15 -3
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +30 -5
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/mime.d.ts +2 -4
- package/dist/core/mime.d.ts.map +1 -1
- package/dist/core/mime.js +3 -8
- package/dist/core/mime.js.map +1 -1
- package/dist/core/path-discovery.d.ts.map +1 -1
- package/dist/core/path-discovery.js +2 -2
- package/dist/core/path-discovery.js.map +1 -1
- package/dist/core/path-utils.d.ts +1 -3
- package/dist/core/path-utils.d.ts.map +1 -1
- package/dist/core/path-utils.js +0 -3
- package/dist/core/path-utils.js.map +1 -1
- package/dist/core/path.d.ts +7 -0
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +69 -26
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +1 -0
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +4 -0
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts +0 -80
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +0 -89
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +8 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +169 -87
- package/dist/core/search.js.map +1 -1
- package/dist/core/store.d.ts +7 -11
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +19 -21
- package/dist/core/store.js.map +1 -1
- package/dist/core/util.d.ts +17 -0
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +19 -0
- package/dist/core/util.js.map +1 -1
- package/dist/instructions.js +1 -1
- package/dist/instructions.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +5 -2
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/batch.d.ts +2 -13
- package/dist/tools/batch.d.ts.map +1 -1
- package/dist/tools/batch.js +6 -18
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +25 -42
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +20 -23
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +11 -23
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +9 -28
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/diff.d.ts.map +1 -1
- package/dist/tools/diff.js +2 -13
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +20 -53
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +3 -28
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-roots.d.ts.map +1 -1
- package/dist/tools/list-roots.js +1 -12
- package/dist/tools/list-roots.js.map +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +19 -53
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +54 -33
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts.map +1 -1
- package/dist/tools/patch.js +4 -24
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/progress.d.ts +0 -2
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +20 -12
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +7 -42
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +20 -43
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/tools/search-text.d.ts.map +1 -1
- package/dist/tools/search-text.js +24 -78
- package/dist/tools/search-text.js.map +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +5 -19
- package/dist/tools/stat.js.map +1 -1
- package/dist/transport/http-policy.d.ts.map +1 -1
- package/dist/transport/http-policy.js +1 -0
- package/dist/transport/http-policy.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +14 -9
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +18 -3
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +3 -3
package/dist/tools/batch.js
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import { processInParallel } from "../core/concurrency.js";
|
|
2
2
|
import { ErrorCode, FsError, Problem } from "../core/errors.js";
|
|
3
3
|
import { PARALLEL_CONCURRENCY } from "../core/util.js";
|
|
4
|
-
function
|
|
5
|
-
if ('path' in args)
|
|
6
|
-
return [{ path: args.path }];
|
|
7
|
-
if ('paths' in args)
|
|
8
|
-
return args.paths.map((path) => ({ path }));
|
|
9
|
-
if ('files' in args)
|
|
10
|
-
return args.files.map(({ path, ...rest }) => ({
|
|
11
|
-
path,
|
|
12
|
-
override: rest,
|
|
13
|
-
}));
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
export async function runOverPaths(args, ctx, defaultErrorCode, perPath) {
|
|
17
|
-
const items = normalizeBatchItems(args);
|
|
4
|
+
export async function runOverPaths(items, ctx, defaultErrorCode, perPath) {
|
|
18
5
|
if (items.length === 0) {
|
|
19
6
|
throw new FsError(ErrorCode.INVALID_INPUT, "runOverPaths: at least one of 'path', 'paths', or 'files' must be provided");
|
|
20
7
|
}
|
|
@@ -23,17 +10,18 @@ export async function runOverPaths(args, ctx, defaultErrorCode, perPath) {
|
|
|
23
10
|
const results = new Array(total);
|
|
24
11
|
const tick = () => {
|
|
25
12
|
completed += 1;
|
|
26
|
-
ctx.onProgress
|
|
13
|
+
ctx.onProgress({ current: completed, total });
|
|
27
14
|
};
|
|
28
15
|
await processInParallel(items, async (item, index) => {
|
|
16
|
+
const path = typeof item === 'string' ? item : item.path;
|
|
29
17
|
try {
|
|
30
18
|
const value = await perPath(item, ctx);
|
|
31
|
-
results[index] = { path
|
|
19
|
+
results[index] = { path, value };
|
|
32
20
|
}
|
|
33
21
|
catch (error) {
|
|
34
22
|
results[index] = {
|
|
35
|
-
path
|
|
36
|
-
error: Problem.fromUnknown(error, defaultErrorCode,
|
|
23
|
+
path,
|
|
24
|
+
error: Problem.fromUnknown(error, defaultErrorCode, path),
|
|
37
25
|
};
|
|
38
26
|
}
|
|
39
27
|
finally {
|
package/dist/tools/batch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/tools/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/tools/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAUvD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAuB,EACvB,GAAY,EACZ,gBAA2B,EAC3B,OAAyD;IAEzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,OAAO,CACf,SAAS,CAAC,aAAa,EACvB,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,MAAM,OAAO,GAA8B,IAAI,KAAK,CAA0B,KAAK,CAAC,CAAC;IAErF,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,SAAS,IAAI,CAAC,CAAC;QACf,GAAG,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC;IAEF,MAAM,iBAAiB,CACrB,KAAK,EACL,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACpB,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QACnC,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,OAAO,CAAC,KAAK,CAAC,GAAG;gBACf,IAAI;gBACJ,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,CAAC;aAC1D,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,IAAI,EAAE,CAAC;QACT,CAAC;IACH,CAAC,EACD,oBAAoB,EACpB,GAAG,CAAC,MAAM,CACX,CAAC;IAEF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC;YAAE,SAAS,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO;QACL,OAAO;QACP,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,GAAG,SAAS,EAAE;KACzD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAG9B;IACC,OAAO,OAAO,CAAC,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC;AAC/D,CAAC","sourcesContent":["import { processInParallel } from '../core/concurrency.ts';\nimport { ErrorCode, FsError, Problem } from '../core/errors.ts';\nimport { PARALLEL_CONCURRENCY } from '../core/util.ts';\nimport type { ToolCtx } from './define.ts';\n\nexport type PerPathResult<T> = { path: string; value: T } | { path: string; error: Problem };\n\nexport interface BatchResult<T> {\n results: PerPathResult<T>[];\n summary: { total: number; succeeded: number; failed: number };\n}\n\nexport async function runOverPaths<TItem extends string | { path: string }, TPerPath>(\n items: readonly TItem[],\n ctx: ToolCtx,\n defaultErrorCode: ErrorCode,\n perPath: (item: TItem, ctx: ToolCtx) => Promise<TPerPath>,\n): Promise<BatchResult<TPerPath>> {\n if (items.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n \"runOverPaths: at least one of 'path', 'paths', or 'files' must be provided\",\n );\n }\n\n const total = items.length;\n let completed = 0;\n const results: PerPathResult<TPerPath>[] = new Array<PerPathResult<TPerPath>>(total);\n\n const tick = (): void => {\n completed += 1;\n ctx.onProgress({ current: completed, total });\n };\n\n await processInParallel(\n items,\n async (item, index) => {\n const path = typeof item === 'string' ? item : item.path;\n try {\n const value = await perPath(item, ctx);\n results[index] = { path, value };\n } catch (error: unknown) {\n results[index] = {\n path,\n error: Problem.fromUnknown(error, defaultErrorCode, path),\n };\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n let succeeded = 0;\n for (const result of results) {\n if (!('error' in result)) succeeded += 1;\n }\n\n return {\n results,\n summary: { total, succeeded, failed: total - succeeded },\n };\n}\n\n/**\n * A batch where every requested item failed produced no work at all, so it is a\n * failed call - `isError` must say so. Partial failure is deliberately NOT an\n * error: the per-item entries carry which failed, and the succeeded ones really\n * were done. Zero items is not a failure either.\n */\nexport function isTotalFailure(summary: {\n readonly total: number;\n readonly failed: number;\n}): boolean {\n return summary.total > 0 && summary.failed === summary.total;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/tools/create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/tools/create.ts"],"names":[],"mappings":"AA+GA,eAAO,MAAM,MAAM,mCAmNjB,CAAC"}
|
package/dist/tools/create.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { basename, dirname } from 'node:path';
|
|
2
2
|
import * as z from 'zod/v4';
|
|
3
3
|
import { processInParallel } from "../core/concurrency.js";
|
|
4
|
-
import { ErrorCode, formatUnknownErrorMessage, FsError, rethrowIfAborted } from "../core/errors.js";
|
|
4
|
+
import { ErrorCode, formatUnknownErrorMessage, FsError, rethrowIfAborted, } from "../core/errors.js";
|
|
5
5
|
import { buildWrittenFileMeta, writtenFileLinks } from "../core/file-uri.js";
|
|
6
6
|
import { countFileLines, destExists } from "../core/fs.js";
|
|
7
7
|
import { confirmKey, describeRefusal, pendingRoundTrip, readAcceptedChoice, } from "../core/input-required.js";
|
|
8
8
|
import { detectMimeFromContent, MIME_SAMPLE_SIZE } from "../core/mime.js";
|
|
9
9
|
import { isSamePath } from "../core/path-utils.js";
|
|
10
|
-
import {
|
|
10
|
+
import { RequiredPath } from "../core/schema.js";
|
|
11
11
|
import { getMaxTextFileSize, PARALLEL_CONCURRENCY } from "../core/util.js";
|
|
12
12
|
import { isTotalFailure, runOverPaths } from "./batch.js";
|
|
13
13
|
import { defineTool } from "./define.js";
|
|
@@ -29,26 +29,13 @@ const CreateFileItemSchema = z.strictObject({
|
|
|
29
29
|
.optional()
|
|
30
30
|
.describe('Replace an existing file without asking the user; without it an existing file prompts for confirmation'),
|
|
31
31
|
});
|
|
32
|
-
const CreateFileResultSchema = z.strictObject({
|
|
33
|
-
path: z.string().describe('Resolved absolute path of the created file'),
|
|
34
|
-
size: NonNegInt.describe('File size in bytes after writing'),
|
|
35
|
-
lineCount: NonNegInt.describe('Number of lines in the written file'),
|
|
36
|
-
mimeType: z.string().describe('Detected MIME type of the file'),
|
|
37
|
-
kind: FileKind.describe('Broad file kind: text, binary, image, audio, or pdf'),
|
|
38
|
-
resourceUri: z
|
|
39
|
-
.string()
|
|
40
|
-
.optional()
|
|
41
|
-
.describe('Resource URI pointing to the created file content in the resource store; omitted when the resulting file exceeds the text-size cap, which the store would reject'),
|
|
42
|
-
created: IsoDateTime.describe('File creation timestamp (ISO 8601 UTC)'),
|
|
43
|
-
modified: IsoDateTime.describe('File last-modification timestamp (ISO 8601 UTC)'),
|
|
44
|
-
});
|
|
45
32
|
const CreateInputSchema = z
|
|
46
33
|
.strictObject({
|
|
47
34
|
files: z
|
|
48
35
|
.array(CreateFileItemSchema)
|
|
49
36
|
.min(1)
|
|
50
37
|
.max(100)
|
|
51
|
-
.describe('List of files to create (max 100); each entry requires path and content'),
|
|
38
|
+
.describe('List of files to create (max 100); each entry requires path and content; combined content is capped at the file-size limit'),
|
|
52
39
|
})
|
|
53
40
|
.superRefine((value, ctx) => {
|
|
54
41
|
// Entries write in parallel and each replaces its file whole, so two
|
|
@@ -65,17 +52,20 @@ const CreateInputSchema = z
|
|
|
65
52
|
});
|
|
66
53
|
}
|
|
67
54
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
55
|
+
// One call's content is what one inbound message carries; the transports
|
|
56
|
+
// size their message bound from this same limit (getMaxInboundMessageBytes),
|
|
57
|
+
// so a batch that passes here always fits on the wire. Counted in the same
|
|
58
|
+
// UTF-16 units as the per-file refine above.
|
|
59
|
+
const total = value.files.reduce((sum, file) => sum + file.content.length, 0);
|
|
60
|
+
const limit = getMaxTextFileSize();
|
|
61
|
+
if (total > limit) {
|
|
62
|
+
ctx.addIssue({
|
|
63
|
+
code: 'custom',
|
|
64
|
+
path: ['files'],
|
|
65
|
+
message: `combined content is ${String(total)} characters; one create call carries at most ${String(limit)} (FS_MAX_FILE_SIZE). Split the batch.`,
|
|
66
|
+
input: value,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
79
69
|
});
|
|
80
70
|
export const CREATE = defineTool({
|
|
81
71
|
name: 'create',
|
|
@@ -83,12 +73,7 @@ export const CREATE = defineTool({
|
|
|
83
73
|
description: 'Write whole text files: create new ones, overwrite, or append. Missing parent directories are created. ' +
|
|
84
74
|
'edit or patch changes part of an existing file.',
|
|
85
75
|
input: CreateInputSchema,
|
|
86
|
-
|
|
87
|
-
annotations: {
|
|
88
|
-
readOnlyHint: false,
|
|
89
|
-
destructiveHint: true,
|
|
90
|
-
openWorldHint: false,
|
|
91
|
-
},
|
|
76
|
+
readOnlyHint: false,
|
|
92
77
|
accessPaths: (args) => args.files.map((f) => f.path),
|
|
93
78
|
run: async (args, ctx) => {
|
|
94
79
|
// Phase 1 (no mutation): which entries would replace a file that exists
|
|
@@ -123,6 +108,7 @@ export const CREATE = defineTool({
|
|
|
123
108
|
op: 'create',
|
|
124
109
|
pending: pendingSorted,
|
|
125
110
|
requestState: ctx.requestState,
|
|
111
|
+
droppedInputResponseKeys: ctx.droppedInputResponseKeys,
|
|
126
112
|
clientCapabilities: ctx.clientCapabilities,
|
|
127
113
|
serverCtx: ctx.serverCtx,
|
|
128
114
|
buildInputs: (paths) => paths.map((target, i) => ({
|
|
@@ -134,8 +120,7 @@ export const CREATE = defineTool({
|
|
|
134
120
|
if (round !== undefined)
|
|
135
121
|
return round;
|
|
136
122
|
}
|
|
137
|
-
const batch = await runOverPaths(
|
|
138
|
-
const content = override?.content ?? '';
|
|
123
|
+
const batch = await runOverPaths(args.files, ctx, ErrorCode.UNKNOWN, async ({ path, content, append }) => {
|
|
139
124
|
const pendingPath = pendingByPath.get(path);
|
|
140
125
|
if (pendingPath !== undefined) {
|
|
141
126
|
const key = confirmKey(pendingSorted.indexOf(pendingPath));
|
|
@@ -143,7 +128,7 @@ export const CREATE = defineTool({
|
|
|
143
128
|
if (choice === 'skip')
|
|
144
129
|
return { skipped: path };
|
|
145
130
|
if (choice !== 'overwrite') {
|
|
146
|
-
throw new FsError(ErrorCode.CANCELLED, `create cancelled: overwrite of "${path}" was ${describeRefusal(ctx.inputResponses, key)}`, path);
|
|
131
|
+
throw new FsError(ErrorCode.CANCELLED, `create cancelled: overwrite of "${path}" was ${describeRefusal(ctx.inputResponses, key, ctx.droppedInputResponseKeys)}`, path);
|
|
147
132
|
}
|
|
148
133
|
}
|
|
149
134
|
await ctx.fs.mkdir(dirname(path), { recursive: true });
|
|
@@ -158,7 +143,7 @@ export const CREATE = defineTool({
|
|
|
158
143
|
let meta;
|
|
159
144
|
let created;
|
|
160
145
|
let modified;
|
|
161
|
-
if (
|
|
146
|
+
if (append) {
|
|
162
147
|
const appended = await ctx.fs.appendFile(path, content, {
|
|
163
148
|
signal: ctx.signal,
|
|
164
149
|
});
|
|
@@ -186,7 +171,7 @@ export const CREATE = defineTool({
|
|
|
186
171
|
lineCount = await countFileLines(appended.validPath);
|
|
187
172
|
}
|
|
188
173
|
catch (error) {
|
|
189
|
-
ctx.log
|
|
174
|
+
ctx.log('warning', `create append: result metadata degraded for ${appended.validPath}: ${formatUnknownErrorMessage(error)}`, 'create');
|
|
190
175
|
// A POSIX mode-0222 file appends fine but cannot be opened 'r';
|
|
191
176
|
// fall back to the chunk for MIME (an approximation) and report
|
|
192
177
|
// the line count as unknown-as-zero rather than fail the append.
|
|
@@ -200,9 +185,7 @@ export const CREATE = defineTool({
|
|
|
200
185
|
lineCount,
|
|
201
186
|
mimeType,
|
|
202
187
|
kind,
|
|
203
|
-
...writtenFileLinks(appended.validPath, mimeType, stats?.size,
|
|
204
|
-
resourceStore: ctx.resourceStore,
|
|
205
|
-
}),
|
|
188
|
+
...writtenFileLinks(appended.validPath, mimeType, stats?.size, false, stats?.mtime.toISOString()),
|
|
206
189
|
};
|
|
207
190
|
created = (stats?.birthtime ?? EPOCH).toISOString();
|
|
208
191
|
modified = (stats?.mtime ?? EPOCH).toISOString();
|
|
@@ -218,7 +201,7 @@ export const CREATE = defineTool({
|
|
|
218
201
|
meta = buildWrittenFileMeta({
|
|
219
202
|
validPath: written.validPath,
|
|
220
203
|
content,
|
|
221
|
-
|
|
204
|
+
lastModified: modified,
|
|
222
205
|
});
|
|
223
206
|
}
|
|
224
207
|
const file = {
|
package/dist/tools/create.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/tools/create.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename, dirname } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport { ErrorCode, formatUnknownErrorMessage, FsError, rethrowIfAborted } from '../core/errors.ts';\nimport { buildWrittenFileMeta, writtenFileLinks, type WrittenFileMeta } from '../core/file-uri.ts';\nimport { countFileLines, destExists, type Stats } from '../core/fs.ts';\nimport {\n confirmKey,\n describeRefusal,\n pendingRoundTrip,\n readAcceptedChoice,\n} from '../core/input-required.ts';\nimport { detectMimeFromContent, MIME_SAMPLE_SIZE } from '../core/mime.ts';\nimport { isSamePath } from '../core/path-utils.ts';\nimport {\n FileKind,\n IsoDateTime,\n NonNegInt,\n PathFailureSchema,\n RequiredPath,\n} from '../core/schema.ts';\nimport { getMaxTextFileSize, PARALLEL_CONCURRENCY } from '../core/util.ts';\nimport { isTotalFailure, runOverPaths } from './batch.ts';\nimport { defineTool } from './define.ts';\n\nconst EPOCH = new Date(0);\n\nconst CreateFileItemSchema = z.strictObject({\n path: RequiredPath.describe('Absolute path where the file will be created'),\n content: z\n .string()\n .refine((val) => val.length <= getMaxTextFileSize(), {\n message: 'Content exceeds maximum allowed text file size',\n })\n .describe('Text content to write, verbatim.'),\n append: z\n .boolean()\n .optional()\n .describe(\n 'Append the content to the end of the file instead of overwriting; creates the file if it does not exist',\n ),\n overwrite: z\n .boolean()\n .optional()\n .describe(\n 'Replace an existing file without asking the user; without it an existing file prompts for confirmation',\n ),\n});\n\nconst CreateFileResultSchema = z.strictObject({\n path: z.string().describe('Resolved absolute path of the created file'),\n size: NonNegInt.describe('File size in bytes after writing'),\n lineCount: NonNegInt.describe('Number of lines in the written file'),\n mimeType: z.string().describe('Detected MIME type of the file'),\n kind: FileKind.describe('Broad file kind: text, binary, image, audio, or pdf'),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'Resource URI pointing to the created file content in the resource store; omitted when the resulting file exceeds the text-size cap, which the store would reject',\n ),\n created: IsoDateTime.describe('File creation timestamp (ISO 8601 UTC)'),\n modified: IsoDateTime.describe('File last-modification timestamp (ISO 8601 UTC)'),\n});\n\nconst CreateInputSchema = z\n .strictObject({\n files: z\n .array(CreateFileItemSchema)\n .min(1)\n .max(100)\n .describe('List of files to create (max 100); each entry requires path and content'),\n })\n .superRefine((value, ctx) => {\n // Entries write in parallel and each replaces its file whole, so two\n // entries for one path race: the last rename wins and the other's\n // content is lost while both report success. One entry per file.\n for (const [index, file] of value.files.entries()) {\n const first = value.files.findIndex((other) => isSamePath(other.path, file.path));\n if (first < index) {\n ctx.addIssue({\n code: 'custom',\n path: ['files', index, 'path'],\n message: `duplicate of files[${String(first)}].path; one entry per file`,\n input: value,\n });\n }\n }\n });\n\ntype CreateFailureItem = z.infer<typeof PathFailureSchema>;\n\nconst CreateOutputSchema = z.strictObject({\n files: z.array(CreateFileResultSchema).describe('Successfully created files'),\n failures: z\n .array(PathFailureSchema)\n .optional()\n .describe('Files that failed to create with per-file error details'),\n skipped: z\n .array(z.string())\n .optional()\n .describe('Paths left untouched because the user chose Skip'),\n});\n\ntype CreateFileResult = z.infer<typeof CreateFileResultSchema>;\n\nexport const CREATE = defineTool({\n name: 'create',\n title: 'Create Files',\n description:\n 'Write whole text files: create new ones, overwrite, or append. Missing parent directories are created. ' +\n 'edit or patch changes part of an existing file.',\n input: CreateInputSchema,\n output: CreateOutputSchema,\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n openWorldHint: false,\n },\n accessPaths: (args) => args.files.map((f) => f.path),\n run: async (args, ctx) => {\n // Phase 1 (no mutation): which entries would replace a file that exists\n // right now? Those need the user's word before anything is written (R14).\n // append never destroys, and overwrite: true is that word given up front.\n // A path that fails validation here is left for runOverPaths to report.\n // A file that appears between rounds needs no re-stat before the write:\n // the retry re-plans, and the grown pending set fails the R9 check.\n const { results: planned } = await processInParallel(\n args.files,\n async (entry) => {\n if (entry.append || entry.overwrite) return undefined;\n try {\n const validPath = await ctx.fs.pathGuard.validatePathForWrite(entry.path);\n return (await destExists(ctx.fs, validPath, 'create')) ? validPath : undefined;\n } catch (error) {\n rethrowIfAborted(error);\n return undefined;\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n const pendingByPath = new Map<string, string>();\n for (const { index, value } of planned) {\n const requested = args.files[index]?.path;\n if (value && requested) pendingByPath.set(requested, value);\n }\n const pendingSorted = [...new Set(pendingByPath.values())].sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op: 'create',\n pending: pendingSorted,\n requestState: ctx.requestState,\n clientCapabilities: ctx.clientCapabilities,\n serverCtx: ctx.serverCtx,\n buildInputs: (paths) =>\n paths.map((target, i) => ({\n key: confirmKey(i),\n message: `\"${target}\" already exists. Overwrite it?`,\n choices: ['overwrite', 'skip'],\n })),\n });\n if (round !== undefined) return round;\n }\n\n const batch = await runOverPaths<\n { content: string; append?: boolean | undefined; overwrite?: boolean | undefined },\n { file: CreateFileResult; resourceLink?: ContentBlock } | { skipped: string }\n >({ files: args.files }, ctx, ErrorCode.UNKNOWN, async ({ path, override }) => {\n const content = override?.content ?? '';\n\n const pendingPath = pendingByPath.get(path);\n if (pendingPath !== undefined) {\n const key = confirmKey(pendingSorted.indexOf(pendingPath));\n const choice = readAcceptedChoice(ctx.inputResponses, key);\n if (choice === 'skip') return { skipped: path };\n if (choice !== 'overwrite') {\n throw new FsError(\n ErrorCode.CANCELLED,\n `create cancelled: overwrite of \"${path}\" was ${describeRefusal(ctx.inputResponses, key)}`,\n path,\n );\n }\n }\n\n await ctx.fs.mkdir(dirname(path), { recursive: true });\n\n // Overwrite: file content == `content`, so content-derived meta is\n // exact and the atomic temp+rename write protects the existing mode.\n // Append: the resulting file is everything that was there plus\n // `content`, so size/created/modified come from a real post-append\n // stat, MIME from the resulting file's leading bytes, and the line\n // count streams the file — reading a multi-GB log back whole is the\n // round-trip append exists to avoid.\n let validPath: string;\n let meta: WrittenFileMeta;\n let created: string;\n let modified: string;\n if (override?.append) {\n const appended = await ctx.fs.appendFile(path, content, {\n signal: ctx.signal,\n });\n validPath = appended.validPath;\n // The append is the commit point: the bytes are on disk, so\n // everything below is best-effort RESULT METADATA, not part of the\n // write. It runs unwired to ctx.signal, and a metadata failure\n // degrades the result instead of failing it — either would report\n // the append as failed and a client retry would append twice. The\n // stat covers the committed validPath, never the input path: a\n // symlink there could have been swapped between write and stat,\n // and the result must describe the file the bytes landed in.\n let stats: Stats | undefined;\n let mimeType: string;\n let kind: FileKind;\n let lineCount = 0;\n try {\n stats = (await ctx.fs.stat(appended.validPath)).stats;\n // Sniff the resulting file's leading bytes, not the appended\n // chunk: text appended to a binary file is still a binary file.\n const sample = await ctx.fs.readLeadingSample(appended.validPath, MIME_SAMPLE_SIZE);\n const mimeInfo = detectMimeFromContent(appended.validPath, sample);\n mimeType = mimeInfo.mimeType;\n kind = mimeInfo.kind;\n lineCount = await countFileLines(appended.validPath);\n } catch (error) {\n ctx.log?.(\n 'warning',\n `create append: result metadata degraded for ${appended.validPath}: ${formatUnknownErrorMessage(error)}`,\n 'create',\n );\n // A POSIX mode-0222 file appends fine but cannot be opened 'r';\n // fall back to the chunk for MIME (an approximation) and report\n // the line count as unknown-as-zero rather than fail the append.\n const mimeInfo = detectMimeFromContent(appended.validPath, content);\n mimeType = mimeInfo.mimeType;\n kind = mimeInfo.kind;\n }\n // A failed stat leaves the size unknown, so nothing is advertised.\n meta = {\n size: stats?.size ?? 0,\n lineCount,\n mimeType,\n kind,\n ...writtenFileLinks(appended.validPath, mimeType, stats?.size, {\n resourceStore: ctx.resourceStore,\n }),\n };\n created = (stats?.birthtime ?? EPOCH).toISOString();\n modified = (stats?.mtime ?? EPOCH).toISOString();\n } else {\n const written = await ctx.fs.writeFile(path, content, {\n signal: ctx.signal,\n });\n validPath = written.validPath;\n const fileStats = (await ctx.fs.stat(path, { signal: ctx.signal })).stats;\n created = fileStats.birthtime.toISOString();\n modified = fileStats.mtime.toISOString();\n meta = buildWrittenFileMeta({\n validPath: written.validPath,\n content,\n resourceStore: ctx.resourceStore,\n });\n }\n\n const file: CreateFileResult = {\n path: validPath,\n size: meta.size,\n lineCount: meta.lineCount,\n mimeType: meta.mimeType,\n kind: meta.kind,\n resourceUri: meta.resourceUri,\n created,\n modified,\n };\n\n return meta.resourceLink ? { file, resourceLink: meta.resourceLink } : { file };\n });\n\n const results: CreateFileResult[] = [];\n const failures: CreateFailureItem[] = [];\n const skipped: string[] = [];\n const links: ContentBlock[] = [];\n for (const r of batch.results) {\n if ('error' in r) {\n failures.push({ path: r.path, error: r.error });\n continue;\n }\n if ('skipped' in r.value) {\n skipped.push(r.value.skipped);\n continue;\n }\n results.push(r.value.file);\n if (r.value.resourceLink) links.push(r.value.resourceLink);\n }\n\n const structured = {\n files: results,\n ...(failures.length > 0 ? { failures } : {}),\n ...(skipped.length > 0 ? { skipped } : {}),\n };\n // No `text` on purpose. The one-line roster this used to build named the\n // paths and dropped every per-file size, line count and error the caller\n // asked `create` for. Supplying no text makes this a data tool, so\n // `define.ts` renders the JSON and keeps it in `structuredContent`.\n if (links.length > 0) {\n return { structured, resources: links, isError: isTotalFailure(batch.summary) };\n }\n\n return { structured, isError: isTotalFailure(batch.summary) };\n },\n progress: (args) => ({\n label: 'Create',\n subject:\n args.files.length === 1\n ? basename(args.files[0]?.path ?? '')\n : `${String(args.files.length)} files`,\n }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/tools/create.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename, dirname } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport {\n ErrorCode,\n formatUnknownErrorMessage,\n FsError,\n type Problem,\n rethrowIfAborted,\n} from '../core/errors.ts';\nimport { buildWrittenFileMeta, writtenFileLinks, type WrittenFileMeta } from '../core/file-uri.ts';\nimport { countFileLines, destExists, type Stats } from '../core/fs.ts';\nimport {\n confirmKey,\n describeRefusal,\n pendingRoundTrip,\n readAcceptedChoice,\n} from '../core/input-required.ts';\nimport { detectMimeFromContent, type FileKind, MIME_SAMPLE_SIZE } from '../core/mime.ts';\nimport { isSamePath } from '../core/path-utils.ts';\nimport { RequiredPath } from '../core/schema.ts';\nimport { getMaxTextFileSize, PARALLEL_CONCURRENCY } from '../core/util.ts';\nimport { isTotalFailure, runOverPaths } from './batch.ts';\nimport { defineTool } from './define.ts';\n\nconst EPOCH = new Date(0);\n\nconst CreateFileItemSchema = z.strictObject({\n path: RequiredPath.describe('Absolute path where the file will be created'),\n content: z\n .string()\n .refine((val) => val.length <= getMaxTextFileSize(), {\n message: 'Content exceeds maximum allowed text file size',\n })\n .describe('Text content to write, verbatim.'),\n append: z\n .boolean()\n .optional()\n .describe(\n 'Append the content to the end of the file instead of overwriting; creates the file if it does not exist',\n ),\n overwrite: z\n .boolean()\n .optional()\n .describe(\n 'Replace an existing file without asking the user; without it an existing file prompts for confirmation',\n ),\n});\n\ntype CreateFileResult = Omit<WrittenFileMeta, 'resourceLink'> & {\n path: string;\n created: string;\n modified: string;\n};\n\nconst CreateInputSchema = z\n .strictObject({\n files: z\n .array(CreateFileItemSchema)\n .min(1)\n .max(100)\n .describe(\n 'List of files to create (max 100); each entry requires path and content; combined content is capped at the file-size limit',\n ),\n })\n .superRefine((value, ctx) => {\n // Entries write in parallel and each replaces its file whole, so two\n // entries for one path race: the last rename wins and the other's\n // content is lost while both report success. One entry per file.\n for (const [index, file] of value.files.entries()) {\n const first = value.files.findIndex((other) => isSamePath(other.path, file.path));\n if (first < index) {\n ctx.addIssue({\n code: 'custom',\n path: ['files', index, 'path'],\n message: `duplicate of files[${String(first)}].path; one entry per file`,\n input: value,\n });\n }\n }\n // One call's content is what one inbound message carries; the transports\n // size their message bound from this same limit (getMaxInboundMessageBytes),\n // so a batch that passes here always fits on the wire. Counted in the same\n // UTF-16 units as the per-file refine above.\n const total = value.files.reduce((sum, file) => sum + file.content.length, 0);\n const limit = getMaxTextFileSize();\n if (total > limit) {\n ctx.addIssue({\n code: 'custom',\n path: ['files'],\n message: `combined content is ${String(total)} characters; one create call carries at most ${String(limit)} (FS_MAX_FILE_SIZE). Split the batch.`,\n input: value,\n });\n }\n });\n\ninterface CreateFailureItem {\n path: string;\n error: Problem;\n}\n\ninterface CreateOutput {\n files: CreateFileResult[];\n failures?: CreateFailureItem[];\n skipped?: string[];\n}\n\nexport const CREATE = defineTool<typeof CreateInputSchema, CreateOutput>({\n name: 'create',\n title: 'Create Files',\n description:\n 'Write whole text files: create new ones, overwrite, or append. Missing parent directories are created. ' +\n 'edit or patch changes part of an existing file.',\n input: CreateInputSchema,\n readOnlyHint: false,\n accessPaths: (args) => args.files.map((f) => f.path),\n run: async (args, ctx) => {\n // Phase 1 (no mutation): which entries would replace a file that exists\n // right now? Those need the user's word before anything is written (R14).\n // append never destroys, and overwrite: true is that word given up front.\n // A path that fails validation here is left for runOverPaths to report.\n // A file that appears between rounds needs no re-stat before the write:\n // the retry re-plans, and the grown pending set fails the R9 check.\n const { results: planned } = await processInParallel(\n args.files,\n async (entry) => {\n if (entry.append || entry.overwrite) return undefined;\n try {\n const validPath = await ctx.fs.pathGuard.validatePathForWrite(entry.path);\n return (await destExists(ctx.fs, validPath, 'create')) ? validPath : undefined;\n } catch (error) {\n rethrowIfAborted(error);\n return undefined;\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n const pendingByPath = new Map<string, string>();\n for (const { index, value } of planned) {\n const requested = args.files[index]?.path;\n if (value && requested) pendingByPath.set(requested, value);\n }\n const pendingSorted = [...new Set(pendingByPath.values())].sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op: 'create',\n pending: pendingSorted,\n requestState: ctx.requestState,\n droppedInputResponseKeys: ctx.droppedInputResponseKeys,\n clientCapabilities: ctx.clientCapabilities,\n serverCtx: ctx.serverCtx,\n buildInputs: (paths) =>\n paths.map((target, i) => ({\n key: confirmKey(i),\n message: `\"${target}\" already exists. Overwrite it?`,\n choices: ['overwrite', 'skip'],\n })),\n });\n if (round !== undefined) return round;\n }\n\n const batch = await runOverPaths<\n z.infer<typeof CreateFileItemSchema>,\n { file: CreateFileResult; resourceLink?: ContentBlock } | { skipped: string }\n >(args.files, ctx, ErrorCode.UNKNOWN, async ({ path, content, append }) => {\n const pendingPath = pendingByPath.get(path);\n if (pendingPath !== undefined) {\n const key = confirmKey(pendingSorted.indexOf(pendingPath));\n const choice = readAcceptedChoice(ctx.inputResponses, key);\n if (choice === 'skip') return { skipped: path };\n if (choice !== 'overwrite') {\n throw new FsError(\n ErrorCode.CANCELLED,\n `create cancelled: overwrite of \"${path}\" was ${describeRefusal(ctx.inputResponses, key, ctx.droppedInputResponseKeys)}`,\n path,\n );\n }\n }\n\n await ctx.fs.mkdir(dirname(path), { recursive: true });\n\n // Overwrite: file content == `content`, so content-derived meta is\n // exact and the atomic temp+rename write protects the existing mode.\n // Append: the resulting file is everything that was there plus\n // `content`, so size/created/modified come from a real post-append\n // stat, MIME from the resulting file's leading bytes, and the line\n // count streams the file — reading a multi-GB log back whole is the\n // round-trip append exists to avoid.\n let validPath: string;\n let meta: WrittenFileMeta;\n let created: string;\n let modified: string;\n if (append) {\n const appended = await ctx.fs.appendFile(path, content, {\n signal: ctx.signal,\n });\n validPath = appended.validPath;\n // The append is the commit point: the bytes are on disk, so\n // everything below is best-effort RESULT METADATA, not part of the\n // write. It runs unwired to ctx.signal, and a metadata failure\n // degrades the result instead of failing it — either would report\n // the append as failed and a client retry would append twice. The\n // stat covers the committed validPath, never the input path: a\n // symlink there could have been swapped between write and stat,\n // and the result must describe the file the bytes landed in.\n let stats: Stats | undefined;\n let mimeType: string;\n let kind: FileKind;\n let lineCount = 0;\n try {\n stats = (await ctx.fs.stat(appended.validPath)).stats;\n // Sniff the resulting file's leading bytes, not the appended\n // chunk: text appended to a binary file is still a binary file.\n const sample = await ctx.fs.readLeadingSample(appended.validPath, MIME_SAMPLE_SIZE);\n const mimeInfo = detectMimeFromContent(appended.validPath, sample);\n mimeType = mimeInfo.mimeType;\n kind = mimeInfo.kind;\n lineCount = await countFileLines(appended.validPath);\n } catch (error) {\n ctx.log(\n 'warning',\n `create append: result metadata degraded for ${appended.validPath}: ${formatUnknownErrorMessage(error)}`,\n 'create',\n );\n // A POSIX mode-0222 file appends fine but cannot be opened 'r';\n // fall back to the chunk for MIME (an approximation) and report\n // the line count as unknown-as-zero rather than fail the append.\n const mimeInfo = detectMimeFromContent(appended.validPath, content);\n mimeType = mimeInfo.mimeType;\n kind = mimeInfo.kind;\n }\n // A failed stat leaves the size unknown, so nothing is advertised.\n meta = {\n size: stats?.size ?? 0,\n lineCount,\n mimeType,\n kind,\n ...writtenFileLinks(\n appended.validPath,\n mimeType,\n stats?.size,\n false,\n stats?.mtime.toISOString(),\n ),\n };\n created = (stats?.birthtime ?? EPOCH).toISOString();\n modified = (stats?.mtime ?? EPOCH).toISOString();\n } else {\n const written = await ctx.fs.writeFile(path, content, {\n signal: ctx.signal,\n });\n validPath = written.validPath;\n const fileStats = (await ctx.fs.stat(path, { signal: ctx.signal })).stats;\n created = fileStats.birthtime.toISOString();\n modified = fileStats.mtime.toISOString();\n meta = buildWrittenFileMeta({\n validPath: written.validPath,\n content,\n lastModified: modified,\n });\n }\n\n const file: CreateFileResult = {\n path: validPath,\n size: meta.size,\n lineCount: meta.lineCount,\n mimeType: meta.mimeType,\n kind: meta.kind,\n resourceUri: meta.resourceUri,\n created,\n modified,\n };\n\n return meta.resourceLink ? { file, resourceLink: meta.resourceLink } : { file };\n });\n\n const results: CreateFileResult[] = [];\n const failures: CreateFailureItem[] = [];\n const skipped: string[] = [];\n const links: ContentBlock[] = [];\n for (const r of batch.results) {\n if ('error' in r) {\n failures.push({ path: r.path, error: r.error });\n continue;\n }\n if ('skipped' in r.value) {\n skipped.push(r.value.skipped);\n continue;\n }\n results.push(r.value.file);\n if (r.value.resourceLink) links.push(r.value.resourceLink);\n }\n\n const structured = {\n files: results,\n ...(failures.length > 0 ? { failures } : {}),\n ...(skipped.length > 0 ? { skipped } : {}),\n };\n // No `text` on purpose. The one-line roster this used to build named the\n // paths and dropped every per-file size, line count and error the caller\n // asked `create` for. Supplying no text makes this a data tool, so\n // `define.ts` renders the JSON and keeps it in `structuredContent`.\n if (links.length > 0) {\n return { structured, resources: links, isError: isTotalFailure(batch.summary) };\n }\n\n return { structured, isError: isTotalFailure(batch.summary) };\n },\n progress: (args) => ({\n label: 'Create',\n subject:\n args.files.length === 1\n ? basename(args.files[0]?.path ?? '')\n : `${String(args.files.length)} files`,\n }),\n});\n"]}
|
package/dist/tools/define.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientCapabilities, ContentBlock, InputRequiredResult, McpServer, Notification, RegisteredTool, RequestId, RequestMeta, RequestStateAccessor, ServerContext
|
|
1
|
+
import type { ClientCapabilities, ContentBlock, InputRequiredResult, McpServer, Notification, RegisteredTool, RequestId, RequestMeta, RequestStateAccessor, ServerContext } from '@modelcontextprotocol/server';
|
|
2
2
|
import type * as z from 'zod/v4';
|
|
3
3
|
import { ErrorCode } from '../core/errors.ts';
|
|
4
4
|
import type { ProgressCtx } from '../core/fmt.ts';
|
|
@@ -15,11 +15,11 @@ export interface ToolCtx {
|
|
|
15
15
|
readonly traceparent?: string | undefined;
|
|
16
16
|
readonly fs: GuardedFileSystem;
|
|
17
17
|
readonly pageStore: PageSnapshotStore;
|
|
18
|
-
readonly resourceStore: ResourceStore
|
|
18
|
+
readonly resourceStore: ResourceStore;
|
|
19
19
|
/** Emits a log line to stderr via `Logger.emit`, gated by `FS_LOG_LEVEL`. */
|
|
20
|
-
readonly log
|
|
21
|
-
readonly sendNotification
|
|
22
|
-
readonly onProgress
|
|
20
|
+
readonly log: (level: LoggingLevel, data: unknown, logger?: string) => void;
|
|
21
|
+
readonly sendNotification: (notification: Notification) => Promise<void>;
|
|
22
|
+
readonly onProgress: (params: {
|
|
23
23
|
current: number;
|
|
24
24
|
total?: number;
|
|
25
25
|
}) => void;
|
|
@@ -30,6 +30,13 @@ export interface ToolCtx {
|
|
|
30
30
|
* `readAcceptedConfirm`.
|
|
31
31
|
*/
|
|
32
32
|
readonly inputResponses?: Record<string, unknown> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Keys of `inputResponses` entries the SDK dropped on this round because
|
|
35
|
+
* they were not bare results (the `{ method, result }` wrapper some hosts
|
|
36
|
+
* send). `pendingRoundTrip` re-issues a confirmation whose key is listed
|
|
37
|
+
* here once; `describeRefusal` names the cause after that.
|
|
38
|
+
*/
|
|
39
|
+
readonly droppedInputResponseKeys?: readonly string[] | undefined;
|
|
33
40
|
/**
|
|
34
41
|
* Reads the verified multi-round-trip `requestState` for the current round:
|
|
35
42
|
* the decoded `PendingState` the `requestStateCodec` verified, or `undefined`
|
|
@@ -57,7 +64,7 @@ interface ToolDeps {
|
|
|
57
64
|
readonly server: McpServer;
|
|
58
65
|
readonly pathGuard: PathGuard;
|
|
59
66
|
readonly pageStore: PageSnapshotStore;
|
|
60
|
-
readonly resourceStore: ResourceStore
|
|
67
|
+
readonly resourceStore: ResourceStore;
|
|
61
68
|
readonly era?: 'legacy' | 'modern';
|
|
62
69
|
}
|
|
63
70
|
interface RunResult<T> {
|
|
@@ -71,26 +78,16 @@ interface RunResult<T> {
|
|
|
71
78
|
*/
|
|
72
79
|
readonly isError?: boolean;
|
|
73
80
|
}
|
|
74
|
-
|
|
75
|
-
* `ToolAnnotations` with `readOnlyHint` required and `idempotentHint` removed.
|
|
76
|
-
* `readOnlyHint` is optional in the SDK, but MUTATING_TOOL_NAMES derives the
|
|
77
|
-
* `--read-only` gate from it, so a tool that forgets it must be a compile error
|
|
78
|
-
* rather than a silent reclassification. `idempotentHint` is omitted because
|
|
79
|
-
* nothing reads it: `publishedAnnotations` derives the wire set below.
|
|
80
|
-
*/
|
|
81
|
-
type DeclaredAnnotations = Omit<ToolAnnotations, 'idempotentHint'> & {
|
|
82
|
-
readonly readOnlyHint: boolean;
|
|
83
|
-
};
|
|
84
|
-
export interface ToolDef<I extends z.ZodType, O extends z.ZodType> {
|
|
81
|
+
export interface ToolDef<I extends z.ZodType, O extends object> {
|
|
85
82
|
readonly name: string;
|
|
86
83
|
readonly title: string;
|
|
87
84
|
readonly description: string;
|
|
88
85
|
readonly input: I;
|
|
89
|
-
|
|
90
|
-
readonly
|
|
86
|
+
/** Required: drives both the --read-only gate and published annotations. */
|
|
87
|
+
readonly readOnlyHint: boolean;
|
|
91
88
|
readonly timeoutMs?: number;
|
|
92
89
|
readonly progress?: (args: z.infer<I>) => ProgressCtx;
|
|
93
|
-
readonly progressDone?: (args: z.infer<I>, result:
|
|
90
|
+
readonly progressDone?: (args: z.infer<I>, result: O) => Partial<ProgressCtx>;
|
|
94
91
|
readonly defaultErrorCode?: ErrorCode;
|
|
95
92
|
/**
|
|
96
93
|
* The filesystem paths this tool will operate on, extracted from its parsed
|
|
@@ -101,13 +98,13 @@ export interface ToolDef<I extends z.ZodType, O extends z.ZodType> {
|
|
|
101
98
|
* caller-supplied filesystem paths.
|
|
102
99
|
*/
|
|
103
100
|
readonly accessPaths?: (args: z.infer<I>) => readonly string[];
|
|
104
|
-
readonly run: (args: z.infer<I>, ctx: ToolCtx) => Promise<RunResult<
|
|
101
|
+
readonly run: (args: z.infer<I>, ctx: ToolCtx) => Promise<RunResult<O> | InputRequiredResult>;
|
|
105
102
|
}
|
|
106
103
|
export interface DefinedTool {
|
|
107
104
|
readonly name: string;
|
|
108
|
-
readonly
|
|
105
|
+
readonly readOnlyHint: boolean;
|
|
109
106
|
register(deps: ToolDeps): RegisteredTool;
|
|
110
107
|
}
|
|
111
|
-
export declare function defineTool<I extends z.ZodType, O extends
|
|
108
|
+
export declare function defineTool<I extends z.ZodType, O extends object>(def: ToolDef<I, O>): DefinedTool;
|
|
112
109
|
export {};
|
|
113
110
|
//# sourceMappingURL=define.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/tools/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,SAAS,EACT,WAAW,EACX,oBAAoB,EACpB,aAAa,
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/tools/define.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/define.js
CHANGED
|
@@ -42,6 +42,7 @@ function toToolCtx(ctx, deps) {
|
|
|
42
42
|
resourceStore: deps.resourceStore,
|
|
43
43
|
sendNotification: async (notification) => ctx.mcpReq.notify(notification),
|
|
44
44
|
inputResponses: ctx.mcpReq.inputResponses,
|
|
45
|
+
droppedInputResponseKeys: ctx.mcpReq.droppedInputResponseKeys,
|
|
45
46
|
requestState: ctx.mcpReq.requestState,
|
|
46
47
|
serverCtx: ctx,
|
|
47
48
|
...(clientCapabilities ? { clientCapabilities } : {}),
|
|
@@ -62,13 +63,8 @@ function buildSuccessResponse(result) {
|
|
|
62
63
|
const content = [{ type: 'text', text }, ...(result.resources ?? [])];
|
|
63
64
|
return {
|
|
64
65
|
content,
|
|
65
|
-
...(hasText
|
|
66
|
-
?
|
|
67
|
-
// `structured` is always a plain object. Constraining `O` to prove it
|
|
68
|
-
// is not worth the cost: the bound has to thread through five generic
|
|
69
|
-
// sites and still loses to `exactOptionalPropertyTypes` variance on
|
|
70
|
-
// `RunResult`'s optional fields.
|
|
71
|
-
{ _meta: result.structured }
|
|
66
|
+
...(hasText // Result interfaces have named fields; the SDK metadata requires an index signature.
|
|
67
|
+
? { _meta: result.structured }
|
|
72
68
|
: { structuredContent: result.structured }),
|
|
73
69
|
...(result.isError ? { isError: true } : {}),
|
|
74
70
|
};
|
|
@@ -88,10 +84,9 @@ class ToolExecutor {
|
|
|
88
84
|
: ctx.signal;
|
|
89
85
|
this.#progressCtx = def.progress ? def.progress(parsedArgs) : { label: def.title };
|
|
90
86
|
const token = ctx._meta?.progressToken;
|
|
91
|
-
const notify = ctx.sendNotification;
|
|
92
87
|
this.#progressSession = new ProgressSession({
|
|
93
88
|
label: this.#progressCtx.label,
|
|
94
|
-
...(token !== undefined
|
|
89
|
+
...(token !== undefined ? { sink: { toolName, token, notify: ctx.sendNotification } } : {}),
|
|
95
90
|
});
|
|
96
91
|
this.toolCtx = {
|
|
97
92
|
...ctx,
|
|
@@ -169,6 +164,7 @@ class ToolExecutor {
|
|
|
169
164
|
op: 'grant',
|
|
170
165
|
pending: grantDirs,
|
|
171
166
|
requestState: this.toolCtx.requestState,
|
|
167
|
+
droppedInputResponseKeys: this.toolCtx.droppedInputResponseKeys,
|
|
172
168
|
clientCapabilities: this.toolCtx.clientCapabilities,
|
|
173
169
|
serverCtx: this.toolCtx.serverCtx,
|
|
174
170
|
buildInputs: (dirs) => multi
|
|
@@ -245,17 +241,11 @@ export function defineTool(def) {
|
|
|
245
241
|
// Nothing here depends on `deps`, so it is built once per tool definition
|
|
246
242
|
// rather than once per `register` — the HTTP leg registers every tool afresh
|
|
247
243
|
// on each request.
|
|
248
|
-
//
|
|
249
|
-
// load-bearing (MUTATING_TOOL_NAMES derives the --read-only gate from it) and
|
|
250
|
-
// `openWorldHint: false` is a real claim for a filesystem server. The other
|
|
251
|
-
// two describe behavior a read-only tool cannot have, and restate the default
|
|
252
|
-
// for a mutating one — 29 tokens per tool for nothing a client acts on.
|
|
244
|
+
// Filesystem tools are closed-world; only mutating tools are destructive.
|
|
253
245
|
const publishedAnnotations = {
|
|
254
|
-
readOnlyHint: def.
|
|
255
|
-
openWorldHint:
|
|
256
|
-
...(def.
|
|
257
|
-
? {}
|
|
258
|
-
: { destructiveHint: def.annotations.destructiveHint ?? true }),
|
|
246
|
+
readOnlyHint: def.readOnlyHint,
|
|
247
|
+
openWorldHint: false,
|
|
248
|
+
...(def.readOnlyHint ? {} : { destructiveHint: true }),
|
|
259
249
|
};
|
|
260
250
|
const toolDefShape = {
|
|
261
251
|
title: def.title,
|
|
@@ -269,14 +259,12 @@ export function defineTool(def) {
|
|
|
269
259
|
// own text ships its metadata under `_meta` instead — see
|
|
270
260
|
// `buildSuccessResponse`. The two cannot both hold, and the text is what
|
|
271
261
|
// the model reads. `TOOL-SURFACE-001` pins this for the whole surface.
|
|
272
|
-
//
|
|
273
|
-
// `def.output` stays: it types `RunResult<z.infer<O>>`, which is what makes
|
|
274
|
-
// a tool returning the wrong shape a compile error.
|
|
262
|
+
// Result shapes are checked by TypeScript, not unused runtime schemas.
|
|
275
263
|
annotations: publishedAnnotations,
|
|
276
264
|
};
|
|
277
265
|
return {
|
|
278
266
|
name: def.name,
|
|
279
|
-
|
|
267
|
+
readOnlyHint: def.readOnlyHint,
|
|
280
268
|
register(deps) {
|
|
281
269
|
return deps.server.registerTool(def.name, toolDefShape, async (args, ctx) => new ToolExecutor(def.name, toToolCtx(ctx, deps), def, args).execute());
|
|
282
270
|
},
|