Description
headFile() and tailFile() in src/filesystem/lib.js read the file in
fixed 1024-byte chunks and call .toString('utf-8') on each chunk
independently:
const chunk = Buffer.alloc(1024);
...
buffer += chunk.slice(0, result.bytesRead).toString('utf-8');
When a multi-byte UTF-8 character (e.g. CJK characters, which are 3 bytes
each) straddles a 1024-byte chunk boundary, the trailing incomplete bytes
in one chunk are decoded independently from the leading bytes in the next
chunk, producing mojibake (typically U+FFFD replacement characters) at
that position.
Reproduction
- Create a UTF-8 text file containing Japanese (or other CJK) text long
enough that some character boundary falls exactly on a multiple of
1024 bytes.
- Call
head (or tail) with a line count that requires reading past
that 1024-byte boundary.
- Observe corrupted character(s) near the boundary in the returned text.
- A full read of the same file (no head/tail) does not show the
corruption, confirming the bug is specific to the chunked head/tail
path.
Suggested fix
Decode the accumulated byte buffer incrementally using a streaming
decoder (e.g. Node's StringDecoder, which correctly buffers incomplete
trailing multi-byte sequences across chunk boundaries) instead of calling
.toString('utf-8') on each raw chunk independently.
Environment
@modelcontextprotocol/server-filesystem (installed via npm, dist
build dated 2026-07-22)
- macOS, Claude Desktop
Description
headFile()andtailFile()insrc/filesystem/lib.jsread the file infixed 1024-byte chunks and call
.toString('utf-8')on each chunkindependently:
When a multi-byte UTF-8 character (e.g. CJK characters, which are 3 bytes
each) straddles a 1024-byte chunk boundary, the trailing incomplete bytes
in one chunk are decoded independently from the leading bytes in the next
chunk, producing mojibake (typically U+FFFD replacement characters) at
that position.
Reproduction
enough that some character boundary falls exactly on a multiple of
1024 bytes.
head(ortail) with a line count that requires reading pastthat 1024-byte boundary.
corruption, confirming the bug is specific to the chunked head/tail
path.
Suggested fix
Decode the accumulated byte buffer incrementally using a streaming
decoder (e.g. Node's
StringDecoder, which correctly buffers incompletetrailing multi-byte sequences across chunk boundaries) instead of calling
.toString('utf-8')on each raw chunk independently.Environment
@modelcontextprotocol/server-filesystem(installed via npm, distbuild dated 2026-07-22)