How Desktop Commander MCP's readFile Modes Handle Different File Types
readFile() automatically selects URL mode for remote resources or disk mode for local files, then delegates to type-specific handlers that return base64-encoded images, parsed PDFs, Excel sheet data, or plain text with optional line offsets.
The Desktop Commander MCP server provides a unified readFile() API that abstracts away the complexity of reading diverse file formats. According to the wonderwhy-er/DesktopCommanderMCP source code, the function branches into distinct read file modes based on the isUrl option and the target file's extension or MIME type. This article examines each mode's behavior with practical examples from the codebase.
Primary Modes: URL vs. Disk
The entry point in src/tools/filesystem.ts makes an immediate dispatch decision:
export async function readFile(
filePath: string,
options?: ReadOptions
): Promise<FileResult> {
const { isUrl, offset, length, sheet, range } = options ?? {};
return isUrl
? readFileFromUrl(filePath) // 🌐 URL mode
: readFileFromDisk(filePath, { offset, length, sheet, range }); // 📂 Disk mode
}
Both modes return a standardized FileResult containing content (string or Buffer), mimeType, and optional metadata with type-specific flags.
URL Mode: readFileFromUrl()
URL mode fetches remote content with a 30-second timeout (FILE_OPERATION_TIMEOUTS.URL_FETCH) and classifies responses by MIME type.
File Type Handling in URL Mode
| File Type | Detection | Output Format |
|---|---|---|
Content-Type: application/pdf or .pdf extension |
Parsed to markdown, text stored in metadata.pages |
|
| Image | isImageFile(contentType) returns true |
Base64-encoded string with metadata.isImage: true |
| Other | Fallback to text/plain |
Raw UTF-8 text with metadata.isImage: false |
// From src/tools/filesystem.ts
const isImage = isImageFile(contentType);
const isPdf = isPdfFile(contentType) || url.toLowerCase().endsWith('.pdf');
if (isPdf) {
const pdfResult = await parsePdfToMarkdown(url);
return { content: '', mimeType: 'text/plain', metadata: { …pdfResult.metadata, isPdf: true } };
} else if (isImage) {
const buffer = await response.arrayBuffer();
const content = Buffer.from(buffer).toString('base64');
return { content, mimeType: contentType, metadata: { isImage: true } };
} else {
const content = await response.text();
return { content, mimeType: contentType };
}
Images are fully loaded into memory and base64-encoded. PDFs trigger server-side parsing that extracts text per page. All other content streams as text.
Disk Mode: readFileFromDisk() and Handler Architecture
Disk mode validates the path, then routes to a specialized handler via the factory in src/utils/files/factory.ts. Handlers implement the FileHandler interface and respect only the options relevant to their format.
Handler Priority Order
The factory selects handlers in this precedence: DOCX → PDF → Excel → Image → Binary → Text.
Text Files: TextFileHandler
Extensions: .txt, .js, .json, .md, and any non-binary, non-image file.
Respected options:
offset— Starting line number (0-based)length— Number of lines to return (defaults tofileReadLineLimit, typically 1000)
// Example: Read lines 100-119 of a log file
const result = await readFile('server.log', { offset: 100, length: 20 });
Implementation in src/utils/files/text.ts:
- Reads file once under a 3-minute cancellable timeout
- Uses
splitLinesPreservingEndings()to maintain original line endings when slicing - Returns
metadata.lineCountandmetadata.isImage: false
Image Files: ImageFileHandler
Extensions: .png, .jpg, .jpeg, .gif, .webp, etc.
Behavior:
- Reads complete binary into a Buffer
- Returns base64-encoded string (
content: string) - Sets
metadata.isImage: true - Ignores
offset,length,sheet,range— images cannot be partially read as text
const img = await readFile('screenshot.png');
// img.content === 'iVBORw0KGgoAAAANSUhEUgAA...'
// img.metadata.isImage === true
Binary Files: BinaryFileHandler
Used for: Compiled binaries, archives, or any unrecognized format.
Behavior:
- Returns raw
Bufferincontent(not a string) - Sets
metadata.isBinary: true - No partial read support — entire file delivered
Excel Files: ExcelFileHandler
Formats: .xlsx, .xls, .xlsm
Respected options:
| Option | Purpose |
|---|---|
sheet |
Worksheet name (string) or index (number) |
range |
Cell range like "A1:C10" or "Sheet1!A1:C10" |
offset/length |
Row-wise slicing applied after sheet/range selection |
Implementation (src/utils/files/excel.ts):
- Opens workbook with
xlsxlibrary - Extracts specified sheet and range
- Returns JSON-encoded 2D array in
content - Provides
metadata.sheets(available sheet names) andmetadata.isExcelFile
const result = await readFile('sales.xlsx', {
sheet: 'Q4',
range: 'B5:D50',
offset: 0,
length: 10 // First 10 rows of B5:D50
});
const rows = JSON.parse(result.content); // [['Jan', 1000, 1200], ...]
PDF Files: PdfFileHandler
Extension: .pdf
Behavior:
- Delegates to
parsePdfToMarkdown()(shared with URL mode) - Returns empty
contentstring - Text stored in
metadata.pagesarray - Includes
metadata.isPdf,author,title,totalPages
DOCX Files: DocxFileHandler
Extension: .docx
Behavior:
- Extracts raw text using
docxparsing utilities - Returns plain text in
content - Sets
metadata.isDocx: true
Directory Handling: Graceful Fallback
If readFileFromDisk() receives a directory path, it does not throw EISDIR. Instead, it returns a synthetic text listing (lines 555-576 in src/tools/filesystem.ts):
This is a directory, not a file. Use the list_directory tool instead of read_file for directories.
[DIR] subfolder
[FILE] file.ts
[FILE] package.json
This allows tools to gracefully handle mistaken directory paths.
Internal Mode: readFileInternal()
Used by the editor for precise content preservation. Located at lines 893-936 in src/tools/filesystem.ts.
Constraints:
- Text files only — throws on images
- No status line — pure file content
- Preserves exact line endings via
splitLinesPreservingEndings()
// For edit operations requiring byte-accurate reads
import { readFileInternal } from './tools/filesystem.js';
const exactContent = await readFileInternal('src/config.ts', 0, 500);
No Excel, image, or binary support — those require dedicated handlers.
Summary
- URL mode fetches remote resources, auto-detects PDFs/images by MIME type, and returns base64 images or parsed PDF metadata
- Disk mode routes through a handler factory with priority: DOCX → PDF → Excel → Image → Binary → Text
- Text and Excel handlers respect
offset/length/sheet/rangefor partial reads; other handlers ignore unsupported options - Images always return base64 strings; binary files return raw Buffers; PDFs return metadata with page-wise text
- Directories produce synthetic listings instead of errors
readFileInternal()provides editor-grade text reads with exact line-ending preservation
Frequently Asked Questions
Which readFile mode should I use for remote PDFs?
Use URL mode by setting isUrl: true. The function detects PDFs by Content-Type header or .pdf extension, then parses them server-side via parsePdfToMarkdown(). The textual content populates metadata.pages rather than content.
Why does my image return a string instead of binary data?
Both URL and disk modes base64-encode images into strings. This ensures JSON-serializable responses across the MCP protocol. Check metadata.isImage === true to confirm, then decode the string with Buffer.from(content, 'base64') if you need raw bytes.
Can I read a specific cell range from an Excel file on the web?
No — range selection requires disk mode. URL mode treats Excel files as raw text or binary (depending on server response). Download the file first, then use readFile() with sheet and range options for cell-level extraction.
What happens if I pass line offsets to an image file?
The options are silently ignored. The ImageFileHandler does not implement partial reads — it returns the complete base64-encoded image regardless of offset or length parameters. Only TextFileHandler and ExcelFileHandler respect these slicing options.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →