diff --git a/src/filesystem/__tests__/structured-content.test.ts b/src/filesystem/__tests__/structured-content.test.ts index 67da16f901..e409028e77 100644 --- a/src/filesystem/__tests__/structured-content.test.ts +++ b/src/filesystem/__tests__/structured-content.test.ts @@ -96,6 +96,24 @@ describe('structuredContent schema compliance', () => { // The content should contain directory listing info expect(structuredContent.content).toContain('[FILE]'); }); + + // A dangling symlink is listed by readdir but `fs.stat` cannot resolve it, + // so this is the real path to a failing stat. Reporting the entry as size 0 + // makes it indistinguishable from an empty file -- `formatSize` renders both + // as "0 B" -- and adds a fabricated 0 to the combined size. + it('should reject when an entry cannot be stat-ed rather than report it as 0 bytes', async () => { + await fs.symlink(path.join(testDir, 'no-such-target'), path.join(testDir, 'dangling')); + + const result = await client.callTool({ + name: 'list_directory_with_sizes', + arguments: { path: testDir } + }); + + expect(result.isError).toBe(true); + const content = (result.content as Array<{ type: string; text: string }>).map(c => c.text).join('\n'); + expect(content).toMatch(/ENOENT|dangling/); + expect(content).not.toMatch(/dangling\s+0 B/); + }); }); describe('move_file', () => { diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index 51ac523a66..a103dd0fa2 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -485,25 +485,21 @@ server.registerTool( const entries = await fs.readdir(validPath, { withFileTypes: true }); // Get detailed information for each entry + // + // A stat that fails is not an entry of size 0: `formatSize` renders that + // as "0 B", so an unreadable file and an empty one come out identical, and + // the entry's 0 is added to the combined size below. Let it reject, the + // way `list_directory` and `directory_tree` already let `readdir` reject. const detailedEntries = await Promise.all( entries.map(async (entry) => { const entryPath = path.join(validPath, entry.name); - try { - const stats = await fs.stat(entryPath); - return { - name: entry.name, - isDirectory: entry.isDirectory(), - size: stats.size, - mtime: stats.mtime - }; - } catch (error) { - return { - name: entry.name, - isDirectory: entry.isDirectory(), - size: 0, - mtime: new Date(0) - }; - } + const stats = await fs.stat(entryPath); + return { + name: entry.name, + isDirectory: entry.isDirectory(), + size: stats.size, + mtime: stats.mtime + }; }) );