@j0hanz/filesystem-mcp 2.7.5 → 2.8.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.
Files changed (118) hide show
  1. package/README.md +290 -394
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +15 -7
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.d.ts +1 -9
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/concurrency.d.ts +7 -0
  9. package/dist/core/concurrency.d.ts.map +1 -1
  10. package/dist/core/concurrency.js +19 -0
  11. package/dist/core/concurrency.js.map +1 -1
  12. package/dist/core/errors.d.ts +4 -3
  13. package/dist/core/errors.d.ts.map +1 -1
  14. package/dist/core/errors.js +5 -16
  15. package/dist/core/errors.js.map +1 -1
  16. package/dist/core/fs.d.ts +19 -11
  17. package/dist/core/fs.d.ts.map +1 -1
  18. package/dist/core/fs.js +168 -53
  19. package/dist/core/fs.js.map +1 -1
  20. package/dist/core/glob.d.ts +8 -0
  21. package/dist/core/glob.d.ts.map +1 -1
  22. package/dist/core/glob.js +192 -29
  23. package/dist/core/glob.js.map +1 -1
  24. package/dist/core/input-required.d.ts +12 -0
  25. package/dist/core/input-required.d.ts.map +1 -1
  26. package/dist/core/input-required.js +7 -2
  27. package/dist/core/input-required.js.map +1 -1
  28. package/dist/core/observability.d.ts +2 -0
  29. package/dist/core/observability.d.ts.map +1 -1
  30. package/dist/core/observability.js +7 -0
  31. package/dist/core/observability.js.map +1 -1
  32. package/dist/core/path-completer.d.ts.map +1 -1
  33. package/dist/core/path-completer.js +11 -30
  34. package/dist/core/path-completer.js.map +1 -1
  35. package/dist/core/path.d.ts +7 -18
  36. package/dist/core/path.d.ts.map +1 -1
  37. package/dist/core/path.js +22 -55
  38. package/dist/core/path.js.map +1 -1
  39. package/dist/core/read.d.ts +4 -1
  40. package/dist/core/read.d.ts.map +1 -1
  41. package/dist/core/read.js +25 -16
  42. package/dist/core/read.js.map +1 -1
  43. package/dist/core/schema.d.ts +6 -1
  44. package/dist/core/schema.d.ts.map +1 -1
  45. package/dist/core/schema.js +17 -3
  46. package/dist/core/schema.js.map +1 -1
  47. package/dist/core/search.d.ts +7 -0
  48. package/dist/core/search.d.ts.map +1 -1
  49. package/dist/core/search.js +154 -66
  50. package/dist/core/search.js.map +1 -1
  51. package/dist/core/store.d.ts +36 -1
  52. package/dist/core/store.d.ts.map +1 -1
  53. package/dist/core/store.js +71 -6
  54. package/dist/core/store.js.map +1 -1
  55. package/dist/core/util.d.ts +37 -0
  56. package/dist/core/util.d.ts.map +1 -1
  57. package/dist/core/util.js +62 -0
  58. package/dist/core/util.js.map +1 -1
  59. package/dist/index.js +5 -16
  60. package/dist/index.js.map +1 -1
  61. package/dist/resources.d.ts +5 -1
  62. package/dist/resources.d.ts.map +1 -1
  63. package/dist/resources.js +45 -22
  64. package/dist/resources.js.map +1 -1
  65. package/dist/server.d.ts +10 -5
  66. package/dist/server.d.ts.map +1 -1
  67. package/dist/server.js +8 -8
  68. package/dist/server.js.map +1 -1
  69. package/dist/tools/create.d.ts.map +1 -1
  70. package/dist/tools/create.js +93 -90
  71. package/dist/tools/create.js.map +1 -1
  72. package/dist/tools/define.d.ts +10 -6
  73. package/dist/tools/define.d.ts.map +1 -1
  74. package/dist/tools/define.js +49 -16
  75. package/dist/tools/define.js.map +1 -1
  76. package/dist/tools/delete.d.ts.map +1 -1
  77. package/dist/tools/delete.js +39 -21
  78. package/dist/tools/delete.js.map +1 -1
  79. package/dist/tools/edit.d.ts.map +1 -1
  80. package/dist/tools/edit.js +37 -39
  81. package/dist/tools/edit.js.map +1 -1
  82. package/dist/tools/find-files.d.ts.map +1 -1
  83. package/dist/tools/find-files.js +49 -60
  84. package/dist/tools/find-files.js.map +1 -1
  85. package/dist/tools/index.d.ts +2 -10
  86. package/dist/tools/index.d.ts.map +1 -1
  87. package/dist/tools/index.js +1 -8
  88. package/dist/tools/index.js.map +1 -1
  89. package/dist/tools/list.d.ts.map +1 -1
  90. package/dist/tools/list.js +59 -69
  91. package/dist/tools/list.js.map +1 -1
  92. package/dist/tools/move.d.ts.map +1 -1
  93. package/dist/tools/move.js +39 -5
  94. package/dist/tools/move.js.map +1 -1
  95. package/dist/tools/patch.d.ts.map +1 -1
  96. package/dist/tools/patch.js +88 -86
  97. package/dist/tools/patch.js.map +1 -1
  98. package/dist/tools/read.d.ts.map +1 -1
  99. package/dist/tools/read.js +30 -24
  100. package/dist/tools/read.js.map +1 -1
  101. package/dist/tools/replace-text.d.ts.map +1 -1
  102. package/dist/tools/replace-text.js +60 -46
  103. package/dist/tools/replace-text.js.map +1 -1
  104. package/dist/tools/search-text.d.ts.map +1 -1
  105. package/dist/tools/search-text.js +48 -64
  106. package/dist/tools/search-text.js.map +1 -1
  107. package/dist/transport/http.d.ts.map +1 -1
  108. package/dist/transport/http.js +2 -1
  109. package/dist/transport/http.js.map +1 -1
  110. package/dist/transport/shared.d.ts +7 -5
  111. package/dist/transport/shared.d.ts.map +1 -1
  112. package/dist/transport/shared.js +8 -2
  113. package/dist/transport/shared.js.map +1 -1
  114. package/dist/transport/stdio.d.ts +4 -1
  115. package/dist/transport/stdio.d.ts.map +1 -1
  116. package/dist/transport/stdio.js +46 -12
  117. package/dist/transport/stdio.js.map +1 -1
  118. package/package.json +3 -2
@@ -3,9 +3,9 @@ import * as z from 'zod/v4';
3
3
  import { processInParallel } from "../core/concurrency.js";
4
4
  import { ErrorCode, formatUnknownErrorMessage, FsError, rethrowIfAborted, } from "../core/errors.js";
5
5
  import { buildWrittenFileMeta, writtenFileLinks } from "../core/file-uri.js";
6
- import { countFileLines, destExists } from "../core/fs.js";
6
+ import { destExists } from "../core/fs.js";
7
7
  import { confirmKey, describeRefusal, pendingRoundTrip, readAcceptedChoice, } from "../core/input-required.js";
8
- import { detectMimeFromContent, MIME_SAMPLE_SIZE } from "../core/mime.js";
8
+ import { detectMimeFromContent } from "../core/mime.js";
9
9
  import { isSamePath } from "../core/path-utils.js";
10
10
  import { RequiredPath } from "../core/schema.js";
11
11
  import { getMaxTextFileSize, PARALLEL_CONCURRENCY } from "../core/util.js";
@@ -116,7 +116,7 @@ export const CREATE = defineTool({
116
116
  if (round !== undefined)
117
117
  return round;
118
118
  }
119
- const batch = await runOverPaths(args.files, ctx, ErrorCode.UNKNOWN, async ({ path, content, append }) => {
119
+ const batch = await runOverPaths(args.files, ctx, ErrorCode.UNKNOWN, async ({ path, content, append, overwrite }) => {
120
120
  const pendingPath = pendingByPath.get(path);
121
121
  if (pendingPath !== undefined) {
122
122
  const key = confirmKey(pendingSorted.indexOf(pendingPath));
@@ -127,90 +127,18 @@ export const CREATE = defineTool({
127
127
  throw new FsError(ErrorCode.CANCELLED, `create cancelled: overwrite of "${path}" was ${describeRefusal(ctx, key)}`, path);
128
128
  }
129
129
  }
130
- await ctx.fs.mkdir(dirname(path), { recursive: true });
131
- // Overwrite: file content == `content`, so content-derived meta is
132
- // exact and the atomic temp+rename write protects the existing mode.
133
- // Append: the resulting file is everything that was there plus
134
- // `content`, so size/created/modified come from a real post-append
135
- // stat, MIME from the resulting file's leading bytes, and the line
136
- // count streams the file — reading a multi-GB log back whole is the
137
- // round-trip append exists to avoid.
138
- let validPath;
139
- let meta;
140
- let created;
141
- let modified;
142
- if (append) {
143
- const appended = await ctx.fs.appendFile(path, content, {
144
- signal: ctx.signal,
145
- });
146
- validPath = appended.validPath;
147
- // The append is the commit point: the bytes are on disk, so
148
- // everything below is best-effort RESULT METADATA, not part of the
149
- // write. It runs unwired to ctx.signal, and a metadata failure
150
- // degrades the result instead of failing it — either would report
151
- // the append as failed and a client retry would append twice. The
152
- // stat covers the committed validPath, never the input path: a
153
- // symlink there could have been swapped between write and stat,
154
- // and the result must describe the file the bytes landed in.
155
- let stats;
156
- let mimeType;
157
- let kind;
158
- let lineCount = 0;
159
- try {
160
- stats = (await ctx.fs.stat(appended.validPath)).stats;
161
- // Sniff the resulting file's leading bytes, not the appended
162
- // chunk: text appended to a binary file is still a binary file.
163
- const sample = await ctx.fs.readLeadingSample(appended.validPath, MIME_SAMPLE_SIZE);
164
- const mimeInfo = detectMimeFromContent(appended.validPath, sample);
165
- mimeType = mimeInfo.mimeType;
166
- kind = mimeInfo.kind;
167
- lineCount = await countFileLines(appended.validPath);
130
+ // Lock per resolved file so two entries naming one file (e.g. through a
131
+ // symlinked parent) cannot clobber each other.
132
+ return ctx.fs.lockForWrite(path, async (resolvedTarget) => {
133
+ // The file may have appeared since the overwrite set was planned.
134
+ if (!append &&
135
+ !overwrite &&
136
+ pendingPath === undefined &&
137
+ (await destExists(ctx.fs, resolvedTarget, 'create'))) {
138
+ throw new FsError(ErrorCode.INVALID_INPUT, `create refused: "${path}" appeared after overwrite confirmation was checked`, path);
168
139
  }
169
- catch (error) {
170
- ctx.log('warning', `create append: result metadata degraded for ${appended.validPath}: ${formatUnknownErrorMessage(error)}`, 'create');
171
- // A POSIX mode-0222 file appends fine but cannot be opened 'r';
172
- // fall back to the chunk for MIME (an approximation) and report
173
- // the line count as unknown-as-zero rather than fail the append.
174
- const mimeInfo = detectMimeFromContent(appended.validPath, content);
175
- mimeType = mimeInfo.mimeType;
176
- kind = mimeInfo.kind;
177
- }
178
- // A failed stat leaves the size unknown, so nothing is advertised.
179
- meta = {
180
- size: stats?.size ?? 0,
181
- lineCount,
182
- mimeType,
183
- kind,
184
- ...writtenFileLinks(appended.validPath, mimeType, stats?.size, false, stats?.mtime.toISOString()),
185
- };
186
- created = (stats?.birthtime ?? EPOCH).toISOString();
187
- modified = (stats?.mtime ?? EPOCH).toISOString();
188
- }
189
- else {
190
- const written = await ctx.fs.writeFile(path, content, {
191
- signal: ctx.signal,
192
- });
193
- validPath = written.validPath;
194
- const fileStats = (await ctx.fs.stat(path, { signal: ctx.signal })).stats;
195
- created = fileStats.birthtime.toISOString();
196
- modified = fileStats.mtime.toISOString();
197
- meta = buildWrittenFileMeta({
198
- validPath: written.validPath,
199
- content,
200
- lastModified: modified,
201
- });
202
- }
203
- const file = {
204
- path: validPath,
205
- size: meta.size,
206
- lineCount: meta.lineCount,
207
- mimeType: meta.mimeType,
208
- kind: meta.kind,
209
- resourceUri: meta.resourceUri,
210
- created,
211
- modified,
212
- };
213
- return meta.resourceLink ? { file, resourceLink: meta.resourceLink } : { file };
140
+ return createOne(path, content, append, ctx);
141
+ });
214
142
  });
215
143
  const results = [];
216
144
  const failures = [];
@@ -238,10 +166,7 @@ export const CREATE = defineTool({
238
166
  // paths and dropped every per-file size, line count and error the caller
239
167
  // asked `create` for. Supplying no text makes this a data tool, so
240
168
  // `define.ts` renders the JSON and keeps it in `structuredContent`.
241
- if (links.length > 0) {
242
- return { structured, resources: links, isError: isTotalFailure(batch.summary) };
243
- }
244
- return { structured, isError: isTotalFailure(batch.summary) };
169
+ return { structured, resources: links, isError: isTotalFailure(batch.summary) };
245
170
  },
246
171
  progress: (args) => ({
247
172
  label: 'Create',
@@ -250,4 +175,82 @@ export const CREATE = defineTool({
250
175
  : `${String(args.files.length)} files`,
251
176
  }),
252
177
  });
178
+ async function createOne(path, content, append, ctx) {
179
+ await ctx.fs.mkdir(dirname(path), { recursive: true });
180
+ // Overwrite: file content == `content`, so content-derived meta is
181
+ // exact and the atomic temp+rename write protects the existing mode.
182
+ // Append: the resulting file is everything that was there plus
183
+ // `content`, so size/created/modified come from a real post-append
184
+ // stat, MIME from the resulting file's leading bytes, and the line
185
+ // count streams the file — reading a multi-GB log back whole is the
186
+ // round-trip append exists to avoid.
187
+ let validPath;
188
+ let meta;
189
+ let created;
190
+ let modified;
191
+ if (append) {
192
+ const appended = await ctx.fs.appendFile(path, content, {
193
+ signal: ctx.signal,
194
+ });
195
+ validPath = appended.validPath;
196
+ // The append is the commit point: the bytes are on disk, so
197
+ // everything below is best-effort RESULT METADATA, not part of the
198
+ // write. It runs unwired to ctx.signal, and a metadata failure
199
+ // degrades the result instead of failing it — either would report
200
+ // the append as failed and a client retry would append twice. The
201
+ // stat covers the committed validPath, never the input path: a
202
+ // symlink there could have been swapped between write and stat,
203
+ // and the result must describe the file the bytes landed in.
204
+ let stats;
205
+ let mimeType;
206
+ let kind;
207
+ let lineCount = 0;
208
+ try {
209
+ stats = (await ctx.fs.stat(appended.validPath)).stats;
210
+ // Sniff the resulting file's leading bytes, not the appended
211
+ // chunk: text appended to a binary file is still a binary file.
212
+ const sample = await ctx.fs.readLeadingSample(appended.validPath);
213
+ const mimeInfo = detectMimeFromContent(appended.validPath, sample);
214
+ mimeType = mimeInfo.mimeType;
215
+ kind = mimeInfo.kind;
216
+ lineCount = await ctx.fs.countFileLines(appended.validPath);
217
+ }
218
+ catch (error) {
219
+ ctx.log('warning', `create append: result metadata degraded for ${appended.validPath}: ${formatUnknownErrorMessage(error)}`, 'create');
220
+ // A POSIX mode-0222 file appends fine but cannot be opened 'r';
221
+ // fall back to the chunk for MIME (an approximation) and report
222
+ // the line count as unknown-as-zero rather than fail the append.
223
+ const mimeInfo = detectMimeFromContent(appended.validPath, content);
224
+ mimeType = mimeInfo.mimeType;
225
+ kind = mimeInfo.kind;
226
+ }
227
+ // A failed stat leaves the size unknown, so nothing is advertised.
228
+ meta = {
229
+ size: stats?.size ?? 0,
230
+ lineCount,
231
+ mimeType,
232
+ kind,
233
+ ...writtenFileLinks(appended.validPath, mimeType, stats?.size, false, stats?.mtime.toISOString()),
234
+ };
235
+ created = (stats?.birthtime ?? EPOCH).toISOString();
236
+ modified = (stats?.mtime ?? EPOCH).toISOString();
237
+ }
238
+ else {
239
+ const written = await ctx.fs.writeFile(path, content, {
240
+ signal: ctx.signal,
241
+ });
242
+ validPath = written.validPath;
243
+ const fileStats = (await ctx.fs.stat(path, { signal: ctx.signal })).stats;
244
+ created = fileStats.birthtime.toISOString();
245
+ modified = fileStats.mtime.toISOString();
246
+ meta = buildWrittenFileMeta({
247
+ validPath: written.validPath,
248
+ content,
249
+ lastModified: modified,
250
+ });
251
+ }
252
+ const { resourceLink, ...fileMeta } = meta;
253
+ const file = { path: validPath, ...fileMeta, created, modified };
254
+ return resourceLink ? { file, resourceLink } : { file };
255
+ }
253
256
  //# sourceMappingURL=create.js.map
@@ -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 {\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(ctx, {\n op: 'create',\n pending: pendingSorted,\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, 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 (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"]}
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 { 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 } 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, type ToolCtx } 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(ctx, {\n op: 'create',\n pending: pendingSorted,\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, overwrite }) => {\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, key)}`,\n path,\n );\n }\n }\n\n // Lock per resolved file so two entries naming one file (e.g. through a\n // symlinked parent) cannot clobber each other.\n return ctx.fs.lockForWrite(path, async (resolvedTarget) => {\n // The file may have appeared since the overwrite set was planned.\n if (\n !append &&\n !overwrite &&\n pendingPath === undefined &&\n (await destExists(ctx.fs, resolvedTarget, 'create'))\n ) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `create refused: \"${path}\" appeared after overwrite confirmation was checked`,\n path,\n );\n }\n return createOne(path, content, append, ctx);\n });\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 return { structured, resources: links, 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\nasync function createOne(\n path: string,\n content: string,\n append: boolean | undefined,\n ctx: ToolCtx,\n): Promise<{ file: CreateFileResult; resourceLink?: ContentBlock }> {\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);\n const mimeInfo = detectMimeFromContent(appended.validPath, sample);\n mimeType = mimeInfo.mimeType;\n kind = mimeInfo.kind;\n lineCount = await ctx.fs.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 { resourceLink, ...fileMeta } = meta;\n const file: CreateFileResult = { path: validPath, ...fileMeta, created, modified };\n\n return resourceLink ? { file, resourceLink } : { file };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { ClientCapabilities, ContentBlock, InputRequiredResult, McpServer, Notification, RegisteredTool, RequestId, RequestMeta, RequestStateAccessor, ServerContext } from '@modelcontextprotocol/server';
1
+ import type { ClientCapabilities, ContentBlock, InputRequiredResult, McpServer, RegisteredTool, RequestId, 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';
@@ -8,17 +8,13 @@ import type { PathGuard } from '../core/path.ts';
8
8
  import type { PageSnapshotStore, ResourceStore } from '../core/store.ts';
9
9
  export interface ToolCtx {
10
10
  readonly signal: AbortSignal;
11
- readonly _meta?: RequestMeta | undefined;
12
11
  /** The JSON-RPC id of the `tools/call` this context serves; prefixes every log line. */
13
12
  readonly requestId: RequestId;
14
- /** W3C trace context the client put in `_meta`, when it did; rides the log prefix. */
15
- readonly traceparent?: string | undefined;
16
13
  readonly fs: GuardedFileSystem;
17
14
  readonly pageStore: PageSnapshotStore;
18
15
  readonly resourceStore: ResourceStore;
19
16
  /** Emits a log line to stderr via `Logger.emit`, gated by `FS_LOG_LEVEL`. */
20
17
  readonly log: (level: LoggingLevel, data: unknown, logger?: string) => void;
21
- readonly sendNotification: (notification: Notification) => Promise<void>;
22
18
  readonly onProgress: (params: {
23
19
  current: number;
24
20
  total?: number;
@@ -59,13 +55,16 @@ export interface ToolCtx {
59
55
  readonly clientCapabilities?: ClientCapabilities | undefined;
60
56
  /** The SDK request context this call runs under; `pendingRoundTrip` binds minted state to it. */
61
57
  readonly serverCtx: ServerContext;
58
+ /** `stdio` alone has a 10 MiB frame limit; `http` on HTTP, unset on the in-memory test pair. */
59
+ readonly transport?: 'stdio' | 'http' | undefined;
62
60
  }
63
- interface ToolDeps {
61
+ export interface ToolDeps {
64
62
  readonly server: McpServer;
65
63
  readonly pathGuard: PathGuard;
66
64
  readonly pageStore: PageSnapshotStore;
67
65
  readonly resourceStore: ResourceStore;
68
66
  readonly era?: 'legacy' | 'modern';
67
+ readonly transport?: 'stdio' | 'http';
69
68
  }
70
69
  interface RunResult<T> {
71
70
  readonly structured: T;
@@ -99,6 +98,11 @@ export interface ToolDef<I extends z.ZodType, O extends object> {
99
98
  */
100
99
  readonly accessPaths?: (args: z.infer<I>) => readonly string[];
101
100
  readonly run: (args: z.infer<I>, ctx: ToolCtx) => Promise<RunResult<O> | InputRequiredResult>;
101
+ /**
102
+ * What the stdio size refusal tells the caller to do instead, for a tool
103
+ * that knows better than the generic advice (see `refuseOversized`).
104
+ */
105
+ readonly oversizeRemedy?: (result: RunResult<O>) => string;
102
106
  }
103
107
  export interface DefinedTool {
104
108
  readonly name: string;
@@ -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,EAGd,MAAM,8BAA8B,CAAC;AAOtC,OAAO,KAAK,KAAK,CAAC,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,SAAS,EAAsC,MAAM,mBAAmB,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAQlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGzE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5E,QAAQ,CAAC,gBAAgB,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC9D;;;;;OAKG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAClE;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACzD;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC7D,iGAAiG;IACjG,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;CACnC;AAED,UAAU,QAAQ;IAChB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACpC;AAED,UAAU,SAAS,CAAC,CAAC;IACnB,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,MAAM;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,MAAM,EAAE,CAAC;IAC/D,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC;CAC/F;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAE/B,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC;CAC1C;AAwPD,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,WAAW,CA0CjG"}
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/tools/define.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { CLIENT_CAPABILITIES_META_KEY, isInputRequiredResult, TRACEPARENT_META_KEY, } from '@modelcontextprotocol/server';
2
- import { ErrorCode, formatUnknownErrorMessage, Problem } from "../core/errors.js";
2
+ import { ErrorCode, formatUnknownErrorMessage, FsError, Problem } from "../core/errors.js";
3
3
  import { plainMessage } from "../core/fmt.js";
4
4
  import { GuardedFileSystem } from "../core/fs.js";
5
5
  import { confirmKey, pendingRoundTrip, readAcceptedConfirm, readAcceptedMultiChoice, } from "../core/input-required.js";
6
6
  import { Logger, sanitizeLogField } from "../core/observability.js";
7
7
  import { isSamePath } from "../core/path-utils.js";
8
8
  import { withSplitArgsRecovery } from "../core/schema.js";
9
+ import { jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES } from "../core/util.js";
9
10
  import { ProgressSession } from "./progress.js";
10
11
  function toToolCtx(ctx, deps) {
11
12
  // Envelope first, accessor second — the two eras carry this differently.
@@ -33,20 +34,16 @@ function toToolCtx(ctx, deps) {
33
34
  (deps.era === 'legacy' ? {} : undefined);
34
35
  return {
35
36
  signal: ctx.mcpReq.signal,
36
- ...(ctx.mcpReq._meta ? { _meta: ctx.mcpReq._meta } : {}),
37
37
  requestId: ctx.mcpReq.id,
38
- ...(typeof ctx.mcpReq._meta?.[TRACEPARENT_META_KEY] === 'string'
39
- ? { traceparent: ctx.mcpReq._meta[TRACEPARENT_META_KEY] }
40
- : {}),
41
38
  fs: new GuardedFileSystem(deps.pathGuard),
42
39
  pageStore: deps.pageStore,
43
40
  resourceStore: deps.resourceStore,
44
- sendNotification: async (notification) => ctx.mcpReq.notify(notification),
45
41
  inputResponses: ctx.mcpReq.inputResponses,
46
42
  droppedInputResponseKeys: ctx.mcpReq.droppedInputResponseKeys,
47
43
  requestState: ctx.mcpReq.requestState,
48
44
  serverCtx: ctx,
49
45
  ...(clientCapabilities ? { clientCapabilities } : {}),
46
+ ...(deps.transport ? { transport: deps.transport } : {}),
50
47
  };
51
48
  }
52
49
  /**
@@ -84,14 +81,21 @@ class ToolExecutor {
84
81
  ? AbortSignal.any([ctx.signal, AbortSignal.timeout(def.timeoutMs)])
85
82
  : ctx.signal;
86
83
  this.#progressCtx = def.progress ? def.progress(parsedArgs) : { label: def.title };
87
- const token = ctx._meta?.progressToken;
84
+ const { mcpReq } = ctx.serverCtx;
85
+ const token = mcpReq._meta?.progressToken;
86
+ const notify = async (notification) => mcpReq.notify(notification);
88
87
  this.#progressSession = new ProgressSession({
89
88
  label: this.#progressCtx.label,
90
- ...(token !== undefined ? { sink: { toolName, token, notify: ctx.sendNotification } } : {}),
89
+ ...(token !== undefined ? { sink: { toolName, token, notify } } : {}),
91
90
  });
91
+ const traceparent = mcpReq._meta?.[TRACEPARENT_META_KEY];
92
92
  this.toolCtx = {
93
93
  ...ctx,
94
94
  signal: this.signal,
95
+ // Per-call views, so a response refused after `run` can take back the
96
+ // cached result and page snapshot behind a link or cursor it never sent.
97
+ pageStore: ctx.pageStore.forCall(),
98
+ resourceStore: ctx.resourceStore.forCall(),
95
99
  log: (level, data, logger) => {
96
100
  const msg = typeof data === 'string' ? data : String(data);
97
101
  // `[req <id>]` first so one grep finds every line of one call; the
@@ -101,7 +105,7 @@ class ToolExecutor {
101
105
  // The message is flattened too (uncapped): it can carry a resolved
102
106
  // path or OS error text, and input schemas reject CR/LF but not the
103
107
  // Unicode line separators.
104
- const trace = ctx.traceparent ? ` ${sanitizeLogField(ctx.traceparent)}` : '';
108
+ const trace = typeof traceparent === 'string' ? ` ${sanitizeLogField(traceparent)}` : '';
105
109
  const prefix = logger ? `[${logger}] ` : '';
106
110
  const text = sanitizeLogField(msg, Infinity);
107
111
  Logger.emit(level, `[req ${sanitizeLogField(String(ctx.requestId))}${trace}] ${prefix}${text}`);
@@ -119,17 +123,15 @@ class ToolExecutor {
119
123
  };
120
124
  this.#progressSession.set({ ...p, message: plainMessage('tick', tickCtx) });
121
125
  }
122
- async completeProgress(result) {
126
+ completeProgress(result) {
123
127
  const doneCtx = this.def.progressDone
124
128
  ? { ...this.#progressCtx, ...this.def.progressDone(this.parsedArgs, result) }
125
129
  : this.#progressCtx;
126
130
  this.#progressSession.complete(plainMessage('done', doneCtx));
127
- await this.#progressSession.flush();
128
131
  }
129
- async failProgress(error) {
132
+ failProgress(error) {
130
133
  const errMsg = formatUnknownErrorMessage(error);
131
134
  this.#progressSession.fail(plainMessage('fail', { ...this.#progressCtx, error: errMsg }));
132
- await this.#progressSession.flush();
133
135
  const errorText = Problem.toText(error, this.def.defaultErrorCode ?? ErrorCode.UNKNOWN);
134
136
  return {
135
137
  content: [{ type: 'text', text: errorText }],
@@ -205,6 +207,35 @@ class ToolExecutor {
205
207
  }
206
208
  return undefined;
207
209
  }
210
+ /**
211
+ * stdio's SDK reader drops the connection on a message past 10 MiB, so a
212
+ * response that would overflow it becomes TOO_LARGE instead. Only a call
213
+ * that changed nothing can be refused after `run` — a read-only tool, or a
214
+ * dry run (every tool with `dryRun` writes nothing under it); a mutation may
215
+ * already have committed. The whole encoded response counts, duplicated
216
+ * metadata and escaping included; the ceiling leaves room for the envelope.
217
+ */
218
+ refuseOversized(result, response) {
219
+ if (this.toolCtx.transport !== 'stdio')
220
+ return;
221
+ const dryRun = this.parsedArgs.dryRun === true;
222
+ if (!this.def.readOnlyHint && !dryRun)
223
+ return;
224
+ const encodedBytes = jsonBytesOver(response, MAX_OUTBOUND_RESULT_BYTES);
225
+ if (encodedBytes === undefined)
226
+ return;
227
+ // The client never receives the link or cursor these back, so they would
228
+ // only push live entries out of the stores' caps.
229
+ this.toolCtx.resourceStore.discard();
230
+ this.toolCtx.pageStore.discard();
231
+ const remedy = this.def.oversizeRemedy?.(result) ??
232
+ (dryRun
233
+ ? 'Nothing was written. Preview fewer files or a smaller file per call, or connect over HTTP.'
234
+ : 'Reduce the requested output or connect over HTTP.');
235
+ // The remedy stands in for TOO_LARGE's default head/tail advice, which
236
+ // fits neither media nor a dry run.
237
+ throw new FsError(ErrorCode.TOO_LARGE, `${this.def.name} result too large to return (${String(encodedBytes)} bytes once JSON-encoded, over the ${String(MAX_OUTBOUND_RESULT_BYTES)}-byte stdio message limit).`, undefined, undefined, remedy);
238
+ }
208
239
  async execute() {
209
240
  try {
210
241
  // Access-grant pre-check before any filesystem touch (R7/R8/R9). A
@@ -223,11 +254,13 @@ class ToolExecutor {
223
254
  if (isInputRequiredResult(result)) {
224
255
  return result;
225
256
  }
226
- await this.completeProgress(result.structured);
227
- return buildSuccessResponse(result);
257
+ const response = buildSuccessResponse(result);
258
+ this.refuseOversized(result, response);
259
+ this.completeProgress(result.structured);
260
+ return response;
228
261
  }
229
262
  catch (error) {
230
- return await this.failProgress(error);
263
+ return this.failProgress(error);
231
264
  }
232
265
  finally {
233
266
  await this.#progressSession.flush();