@j0hanz/filesystem-mcp 1.10.0 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -440,13 +440,14 @@ export function decodeOffsetCursor(cursor) {
440
440
  parsed !== null &&
441
441
  typeof parsed.offset === 'number') {
442
442
  const { offset } = parsed;
443
- return Number.isInteger(offset) && offset >= 0 ? offset : 0;
443
+ if (Number.isInteger(offset) && offset >= 0)
444
+ return offset;
444
445
  }
445
446
  }
446
447
  catch {
447
- // ignore malformed cursor
448
+ // fall through to throw
448
449
  }
449
- return 0;
450
+ throw new McpError(ErrorCode.E_INVALID_INPUT, `Invalid cursor: the cursor value is malformed or corrupted. Request the first page without a cursor.`);
450
451
  }
451
452
  export function buildBatchPathContext(paths, unitLabel = 'paths') {
452
453
  const normalizedLabel = paths.length === 1 ? unitLabel.replace(/s$/i, '') : unitLabel;
@@ -35,24 +35,18 @@ async function handleGetMultipleFileInfo(args, signal, onProgress) {
35
35
  ...(signal ? { signal } : {}),
36
36
  ...(onProgress ? { onProgress } : {}),
37
37
  });
38
- const structuredResults = [];
39
- const textBlocks = [];
40
- for (const entry of result.results) {
41
- structuredResults.push({
42
- path: entry.path,
43
- info: entry.info ? buildFileInfoPayload(entry.info) : undefined,
44
- error: entry.error,
45
- });
46
- if (entry.error) {
47
- textBlocks.push(`${entry.path}: ${entry.error}`);
48
- }
49
- else if (entry.info) {
50
- textBlocks.push(formatFileInfoDetail(entry.info));
51
- }
52
- else {
53
- textBlocks.push(entry.path);
54
- }
55
- }
38
+ const structuredResults = result.results.map((entry) => ({
39
+ path: entry.path,
40
+ info: entry.info ? buildFileInfoPayload(entry.info) : undefined,
41
+ error: entry.error,
42
+ }));
43
+ const text = result.results
44
+ .map((entry) => entry.error
45
+ ? `${entry.path}: ${entry.error}`
46
+ : entry.info
47
+ ? formatFileInfoDetail(entry.info)
48
+ : entry.path)
49
+ .join('\n\n');
56
50
  const structured = {
57
51
  ok: true,
58
52
  results: structuredResults,
@@ -62,7 +56,6 @@ async function handleGetMultipleFileInfo(args, signal, onProgress) {
62
56
  failed: result.summary.failed,
63
57
  },
64
58
  };
65
- const text = textBlocks.join('\n\n');
66
59
  return buildToolResponse(text, structured);
67
60
  }
68
61
  export function registerGetMultipleFileInfoTool(server, options = {}) {
@@ -14,7 +14,7 @@ export const GET_FILE_INFO_TOOL = {
14
14
  inputSchema: GetFileInfoInputSchema,
15
15
  outputSchema: GetFileInfoOutputSchema,
16
16
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
17
- taskSupport: 'optional',
17
+ taskSupport: 'forbidden',
18
18
  nuances: ['Use before read/search when file size/type uncertainty exists.'],
19
19
  };
20
20
  function formatFileInfoDetails(info) {
@@ -324,7 +324,9 @@ function tryRegisterToolTask(server, toolName, toolDef, taskHandler, iconInfo) {
324
324
  const existingExecution = def.execution ?? {};
325
325
  const taskSupport = def.taskSupport ??
326
326
  existingExecution.taskSupport ??
327
- 'optional';
327
+ 'forbidden';
328
+ if (taskSupport === 'forbidden')
329
+ return false;
328
330
  tasks.registerToolTask(toolName, withDefaultIcons({ ...toolDef, execution: { ...existingExecution, taskSupport } }, iconInfo), taskHandler);
329
331
  return true;
330
332
  }
@@ -17,18 +17,18 @@ export const WRITE_FILE_TOOL = {
17
17
  gotchas: [
18
18
  '`write` replaces ALL existing content — use `edit` for partial updates.',
19
19
  ],
20
- taskSupport: 'optional',
20
+ taskSupport: 'forbidden',
21
21
  };
22
22
  async function handleWriteFile(args, signal) {
23
23
  const validPath = await validatePathForWrite(args.path, signal);
24
24
  // Ensure parent directory exists
25
25
  await withAbort(fs.mkdir(path.dirname(validPath), { recursive: true }), signal);
26
26
  await atomicWriteFile(validPath, args.content, { encoding: 'utf-8', signal });
27
- const stats = await withAbort(fs.stat(validPath), signal);
27
+ const bytesWritten = Buffer.byteLength(args.content, 'utf-8');
28
28
  return buildToolResponse(`Successfully wrote to file: ${args.path}`, {
29
29
  ok: true,
30
30
  path: validPath,
31
- bytesWritten: stats.size,
31
+ bytesWritten,
32
32
  });
33
33
  }
34
34
  export function registerWriteFileTool(server, options = {}) {
@@ -50,7 +50,7 @@ export function registerWriteFileTool(server, options = {}) {
50
50
  const sc = result.structuredContent;
51
51
  if (!sc.ok)
52
52
  return `🛠 write: ${name} • failed`;
53
- return `🛠 write: ${name} • ${formatBytes(sc.bytesWritten ?? 0)} written`;
53
+ return `🛠 write: ${name} • ${formatBytes(sc.bytesWritten ?? 0)}`;
54
54
  },
55
55
  });
56
56
  const validatedHandler = withValidatedArgs(WriteFileInputSchema, wrappedHandler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/filesystem-mcp",
3
- "version": "1.10.0",
3
+ "version": "1.11.1",
4
4
  "mcpName": "io.github.j0hanz/filesystem-mcp",
5
5
  "description": "A local filesystem MCP server that lets LLMs and AI agents read, write, search, diff, patch, and manage files safely and efficiently. Built for reliable, structured, and controlled filesystem interaction.",
6
6
  "type": "module",