# Understanding fileWriteLineLimit in DesktopCommanderMCP: Why Chunked Writes Are Enforced

> Discover fileWriteLineLimit in DesktopCommanderMCP. Learn why this 10 MiB limit enforces chunked writes to prevent UI blocking, reduce memory usage, and boost reliability for large files.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: deep-dive
- Published: 2026-08-03

---

**The `fileWriteLineLimit` in DesktopCommanderMCP is a 10 MiB threshold (`LINE_COUNT_LIMIT`) that triggers chunked writes to prevent UI-blocking I/O, reduce memory pressure, and improve reliability when handling large files.**

The DesktopCommanderMCP codebase implements protective thresholds for file operations to ensure smooth performance even when dealing with massive text files. These safeguards are particularly important for a desktop application where unresponsive UI directly impacts user experience.

## What Is fileWriteLineLimit?

The term refers to a pair of constants defined in [`src/utils/files/text.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/text.ts) that govern how the system treats large files:

| Constant | Value | Purpose |
|----------|-------|---------|
| `FILE_SIZE_LIMITS.LARGE_FILE_THRESHOLD` | 10 MiB | Files exceeding this size receive special handling (line counting disabled, streaming reads). |
| `FILE_SIZE_LIMITS.LINE_COUNT_LIMIT` | 10 MiB | Same threshold used specifically for the line-counting decision boundary. |

When a write request arrives, the `TextFileHandler.write` method checks these limits. For files under the threshold, it can safely perform operations like line counting. For larger files, the system bypasses expensive computations and delegates to streaming write methods.

```ts
// Located in src/utils/files/text.ts
export const FILE_SIZE_LIMITS = {
  LARGE_FILE_THRESHOLD: 10 * 1024 * 1024, // 10 MiB
  LINE_COUNT_LIMIT: 10 * 1024 * 1024,     // 10 MiB
} as const;

```

The `write` method itself uses Node's `fs.appendFile` or `fs.writeFile` **without loading the entire target file into memory**, regardless of file size. This streaming approach is the first layer of protection.

## Why Force Chunked Writes?

The DesktopCommanderMCP architecture enforces chunked writes through a secondary mechanism in [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts). This is **not optional** for high-frequency or large-payload operations—it's baked into the tool-history recording system for three critical reasons:

### Prevents UI-Blocking I/O

A single synchronous `writeFileSync` call with a massive payload stalls the Node.js event loop. In a desktop application, this manifests as frozen UI, unresponsive menus, and a degraded user experience. The chunked approach yields control back to the event loop between chunks.

### Reduces Memory Pressure

Instead of buffering an entire large file or massive write payload in RAM, the system maintains only a modest chunk size in memory at any moment. This is essential for long-running desktop sessions where memory leaks or spikes cause system-wide slowdowns.

### Improves Reliability

Intermittent failures—disk full, permission denied, network drive disconnect—affect only the current chunk. The remaining data can be retried or logged without total data loss.

## How Chunked Writes Are Implemented

The implementation lives in [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts). The `ToolCallHistory` class maintains a `writeQueue` array and processes it on a ~100 ms timer:

```ts
// Conceptual structure from src/utils/toolHistory.ts
class ToolCallHistory {
  private writeQueue: string[] = [];
  private flushTimer: NodeJS.Timeout | null = null;
  
  record(entry: ToolCallEntry): void {
    this.writeQueue.push(JSON.stringify(entry));
    this.scheduleFlush();
  }
  
  private scheduleFlush(): void {
    if (this.flushTimer) return;
    this.flushTimer = setTimeout(() => this.flushPendingWrites(), 100);
  }
  
  private async flushPendingWrites(): Promise<void> {
    const batch = this.writeQueue.splice(0, this.writeQueue.length);
    // Writes are batched and streamed to disk
    await this.appendChunked(batch.join('\n'));
    this.flushTimer = null;
  }
  
  async gracefulShutdown(): Promise<void> {
    if (this.flushTimer) clearTimeout(this.flushTimer);
    await this.flushPendingWrites(); // Ensure no data loss
  }
}

```

Key properties of this implementation:
- **Batched small writes** – Multiple rapid calls to `record()` collapse into a single disk operation.
- **Graceful shutdown** – The `flushPendingWrites` method guarantees no queued data is lost on process exit.
- **Timer-driven flushing** – The ~100 ms delay balances latency against I/O efficiency.

## Using the Write Methods

Here's how to interact with the file-writing infrastructure correctly:

```ts
// Example 1: Simple write for small files
// src/utils/files/text.ts — TextFileHandler
import { TextFileHandler } from "./utils/files/text.js";

const handler = new TextFileHandler();
await handler.write("/tmp/config.json", jsonString, "rewrite");

// Example 2: Append mode for logs
await handler.write("/var/log/app.log", logEntry, "append");
// fs.appendFile is used under the hood — no full-file load

```

```ts
// Example 3: High-frequency writes — automatically chunked
// src/utils/toolHistory.ts — ToolCallHistory
import { ToolCallHistory } from "./utils/toolHistory.js";

const history = new ToolCallHistory("/home/user/.mcp/history.log");

// This queues internally; actual disk write may be deferred
history.record({
  tool: "edit_file",
  args: { path: "/tmp/large.txt", changes: hugeDiff },
  timestamp: Date.now()
});

// Or record multiple calls rapidly — they'll batch automatically
for (const change of massiveChangeSet) {
  history.record(change);
}

```

## Contract Enforcement

All file handlers implement the `FileHandler` contract declared in [`src/utils/files/base.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/base.ts). This ensures that:

- Every write operation respects the size thresholds.
- Subclasses cannot accidentally bypass streaming methods.
- The system can swap handlers (text, binary, etc.) without breaking limit guarantees.

## Summary

- **`fileWriteLineLimit`** is the 10 MiB `LINE_COUNT_LIMIT` constant in [`src/utils/files/text.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/text.ts).
- Files below this threshold may have line counts computed; larger files skip this step.
- **Chunked writes are forced** in [`src/utils/toolHistory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/toolHistory.ts) via a batched queue system (~100 ms flush interval).
- The architecture prevents event-loop blocking, limits memory usage, and isolates failure domains.
- The `TextFileHandler.write` method and `ToolCallHistory.record` method are the primary user-facing APIs.

## Frequently Asked Questions

### What happens if I try to write a file larger than 10 MiB?

The write proceeds, but the system disables expensive operations like line counting. In [`src/utils/files/text.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/text.ts), the `write` method still uses streaming `fs.writeFile` or `fs.appendFile`, so the operation succeeds without loading the entire file into memory. However, if you're using `ToolCallHistory`, the payload is automatically broken into chunks and queued.

### Can I adjust the fileWriteLineLimit threshold?

The constants in `FILE_SIZE_LIMITS` are hard-coded in [`src/utils/files/text.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/text.ts). As implemented in wonderwhy-er/DesktopCommanderMCP, there is no runtime configuration option. Modifying the source and rebuilding would be required to change the 10 MiB limit.

### Is chunked writing slower than a single large write?

Slightly, due to the ~100 ms batching delay in `ToolCallHistory`. However, this trade-off is intentional: the latency is imperceptible for human interactions, while the responsiveness and reliability gains are substantial. For `TextFileHandler.write` directly, there's no artificial delay—streams write as fast as the disk accepts data.

### How do I ensure all queued writes complete before my script exits?

Call `history.gracefulShutdown()` (or equivalent flush method) on your `ToolCallHistory` instance. This clears the timer, executes `flushPendingWrites()`, and waits for the final disk operation to complete. Without this step, recent tool calls may remain in memory and never reach the log file.