DesktopCommanderMCP File Handler Factory Architecture: Text, Binary, Image and Document Processing

DesktopCommanderMCP implements a centralized factory pattern in src/utils/files/factory.ts that lazily instantiates specialized file handlers and selects them via a priority-based extension and content detection system.

DesktopCommanderMCP is a Model Context Protocol (MCP) server that provides secure file system access for AI assistants. At its core, the file handler factory architecture enables the server to process diverse formats—from plain text to complex Office documents—through a unified interface while maintaining format-specific capabilities.

Factory Pattern Implementation and Singleton Handlers

The factory module manages handler instances using a singleton pattern with lazy initialization. Rather than creating handlers upfront, the factory declares nullable references and instantiates each handler only upon first request.

In src/utils/files/factory.ts, the factory maintains private references to each handler type:

let textHandler: TextFileHandler | null = null;
let imageHandler: ImageFileHandler | null = null;
let binaryHandler: BinaryFileHandler | null = null;
let excelHandler: ExcelFileHandler | null = null;
let pdfHandler: PdfFileHandler | null = null;
let docxHandler: DocxFileHandler | null = null;

Dedicated getter functions like getTextHandler(), getImageHandler(), and getBinaryHandler() check for existing instances before creating new ones. This approach minimizes memory overhead and startup latency, ensuring handlers exist only when actively needed.

Handler Selection Priority and Detection Strategy

The getFileHandler() function determines the appropriate processor by evaluating candidates in a fixed priority sequence. When you request a handler for a specific path, the factory checks each candidate in order until it finds a match:

  1. DOCX (DocxFileHandler) – Matches .docx extensions
  2. PDF (PdfFileHandler) – Matches .pdf extensions
  3. Excel (ExcelFileHandler) – Matches .xlsx, .xls, .xlsm extensions
  4. Image (ImageFileHandler) – Matches image extensions (PNG, JPEG, GIF, WebP)
  5. Binary (BinaryFileHandler) – Detects binary content via the isBinaryFile library
  6. Text (TextFileHandler) – Default fallback for all remaining files

This hierarchy ensures specialized handlers capture their formats before the generic binary or text handlers process them.

Extension-Based vs. Content-Based Detection

The factory employs two distinct detection strategies. Extension-based handlers (DocxFileHandler, PdfFileHandler, ExcelFileHandler, ImageFileHandler) expose a synchronous canHandle(path) method that performs simple string matching on file extensions.

Content-based detection handles ambiguous cases where extensions are missing or unreliable. The BinaryFileHandler.canHandle() method operates asynchronously, calling the third-party isBinaryFile library to inspect file headers and determine if content is binary rather than text. This check occurs in src/utils/files/binary.ts before the factory falls back to TextFileHandler.

The FileHandler Interface Contract

All concrete handlers implement the common FileHandler interface defined in src/utils/files/base.ts. This contract ensures consistent behavior across file types while allowing specialized implementations.

The interface requires four core methods:

  • canHandle(path: string): boolean | Promise<boolean> – Determines if the handler can process the given file
  • read(path, options?) – Returns a FileResult containing content, MIME type, and optional metadata
  • write(path, content, mode?) – Persists changes to the file system
  • getInfo(path) – Returns a FileInfo object with size, timestamps, type, and handler-specific metadata

Each handler imports this base type (import { FileHandler } from './base.js'), ensuring type safety and polymorphic usage throughout the codebase.

Specialized Handler Implementations

The architecture supports six distinct file processors, each optimized for specific format requirements:

TextFileHandler

The TextFileHandler in src/utils/files/text.ts manages plain-text files with full read/write capabilities and line-count metadata. This handler assumes the factory has already filtered out binary files, operating exclusively on text-encoded content.

ImageFileHandler

ImageFileHandler processes PNG, JPEG, GIF, and WebP formats using the sharp image processing library. Like other extension-based handlers, it validates files by extension before attempting processing.

BinaryFileHandler

When isBinaryFile detects binary content, BinaryFileHandler takes over. Rather than attempting to display binary data, this handler returns an instructional message directing users to invoke start_process with appropriate external tools. This safety mechanism prevents corrupting binary files or overwhelming the context window with encoded data.

ExcelFileHandler

ExcelFileHandler manages spreadsheet formats (.xlsx, .xls, .xlsm) using the exceljs library. It provides structured access to cell data, worksheets, and formatting while abstracting the underlying Excel binary format complexity.

PdfFileHandler

The PDF handler leverages pdfjs-dist to parse document structure and extract text or embedded images. It handles the asynchronous nature of PDF parsing while presenting a synchronous interface to the factory consumer.

DocxFileHandler

DocxFileHandler provides the richest functionality among document processors. In addition to reading DOCX files, it generates document outlines, pretty-prints XML structures, and supports advanced editing via the editRange() method. This handler can perform find-and-replace operations within specific XML ranges, enabling surgical document modifications without rewriting entire files.

Usage Flow and Code Examples

The factory exposes a clean async API that resolves the correct handler regardless of file type. Because getFileHandler() returns a Promise<FileHandler>, callers await resolution and then work with a uniform interface.

Resolving and Reading Files

import { getFileHandler } from './utils/files/factory';

async function preview(path: string) {
  const handler = await getFileHandler(path);
  const { content, mimeType } = await handler.read(path, { offset: 0, length: 20 });
  console.log(`MIME: ${mimeType}\n${content}`);
}

Detecting Specific File Types

The factory exports predicate functions for type checking without full handler resolution:

import { isImageFile, isExcelFile } from './utils/files/factory';

const path = '/tmp/report.xlsx';
if (isExcelFile(path)) {
  console.log('Excel file – will be processed by ExcelFileHandler');
}
if (isImageFile(path)) {
  console.log('Image file – will be processed by ImageFileHandler');
}

Editing DOCX Documents

Advanced handlers like DocxFileHandler expose format-specific methods beyond the base interface:

import { getFileHandler } from './utils/files/factory';

async function replaceInDocx(docxPath: string) {
  const handler = await getFileHandler(docxPath);   // resolves to DocxFileHandler
  const editResult = await handler.editRange(docxPath, '', {
    old_string: '<w:t>old text</w:t>',
    new_string: '<w:t>new text</w:t>',
    expected_replacements: 1,
  });
  console.log(editResult);
}

Summary

  • The file handler factory in src/utils/files/factory.ts centralizes file type detection and handler instantiation using a singleton pattern with lazy initialization.
  • Handler selection follows a strict priority order: DOCX → PDF → Excel → Image → Binary → Text, ensuring specialized processors take precedence over generic handlers.
  • Detection strategies vary between extension-based matching (for known formats) and content-based analysis (using isBinaryFile for ambiguous files).
  • All handlers implement the FileHandler interface from src/utils/files/base.ts, guaranteeing consistent read(), write(), getInfo(), and canHandle() methods.
  • The architecture supports extensible, format-specific capabilities—such as DocxFileHandler.editRange()—while maintaining a uniform consumption API for the rest of the application.

Frequently Asked Questions

How does the factory decide which handler to use for a given file?

The getFileHandler() function evaluates candidates in a fixed priority sequence defined in src/utils/files/factory.ts. It first checks for document types (DOCX, PDF, Excel), then images, then performs binary content detection, and finally falls back to text. The first handler whose canHandle() method returns true wins.

What is the difference between extension-based and content-based detection?

Extension-based handlers like PdfFileHandler and ImageFileHandler simply check file extensions (.pdf, .png) synchronously. Content-based detection occurs in BinaryFileHandler.canHandle(), which asynchronously calls the isBinaryFile library to inspect file headers when extensions are absent or unreliable.

Can I extend the factory to support additional file formats?

Yes. Create a new handler class implementing the FileHandler interface from src/utils/files/base.ts, add a singleton getter in src/utils/files/factory.ts, and insert it into the priority chain within getFileHandler(). The factory's design accommodates new handlers without modifying existing code.

Why does the BinaryFileHandler return instructions instead of file content?

BinaryFileHandler detects executable or encoded binary data that could corrupt the context window or be meaningless as text. Instead of returning raw bytes, it provides instructions to use start_process with external tools. This safety mechanism prevents accidental data corruption and keeps the AI assistant's context focused on processable information.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →