Line Ending Handling in DesktopCommanderMCP: Cross-Platform File Editing Explained
DesktopCommanderMCP preserves original line endings during AI-driven file edits to prevent cross-platform corruption, ensure accurate diff previews, and maintain token efficiency.
DesktopCommanderMCP is a Model Context Protocol (MCP) server that enables large language models to read and modify files on the host filesystem. Because Windows, macOS, and Linux use different line ending conventions, robust line ending handling is essential to prevent silent file corruption and ensure edits apply cleanly regardless of the platform.
Why Line Ending Handling Prevents File Corruption
When an AI model edits a file without respecting the original line ending characters (\r\n, \n, or \r), several critical problems emerge:
-
Cross-platform corruption – Windows tools expect
\r\nwhile Unix-based systems use\n. Changing these conventions can break scripts, configuration files, and source code compilation. -
Incorrect diffs – The edit preview shown in Claude Desktop compares old and new text. Mismatched line endings make the diff appear as if every line changed, obscuring the actual modification.
-
Mixed-ending contamination – Some files intentionally contain mixed line endings. Without detection, edits can introduce a new style, turning a consistent file into an inconsistent one.
-
Token inefficiency – The MCP server streams file content line-by-line. Preserving original endings allows the server to reuse the same line-ending tokenization, reducing unnecessary token consumption.
How DesktopCommanderMCP Detects and Normalizes Line Endings
The implementation relies on a three-stage pipeline: detection, normalization, and preservation. This logic resides primarily in src/utils/lineEndingHandler.ts and is orchestrated by src/tools/edit.ts.
Detecting the Dominant Line Ending Style
The detectLineEnding function in src/utils/lineEndingHandler.ts scans the raw file string to identify the dominant convention.
import { detectLineEnding } from './utils/lineEndingHandler.js';
const style = detectLineEnding(fileContent);
// Returns: '\r\n', '\n', or '\r'
If no line endings are detected, the function falls back to the host operating system’s default. This ensures that new files or single-line documents receive appropriate formatting.
Normalizing Strings to Match File Conventions
Before performing any replacement, both the search and replace strings must match the file’s internal representation. The normalizeLineEndings function collapses all input to LF (\n) first, then rewrites the content to the target style.
import { normalizeLineEndings } from './utils/lineEndingHandler.js';
const normalizedSearch = normalizeLineEndings(userSearch, fileLineEnding);
const normalizedReplace = normalizeLineEndings(userReplace, fileLineEnding);
This normalization guarantees that user input matches the file’s line ending style, enabling accurate string matching regardless of how the user typed the query.
The Edit Flow Architecture
The complete edit workflow in src/tools/edit.ts implements line ending handling through five precise steps:
- Read raw file –
readFileInternalreturns the file exactly as stored, preserving all line endings. - Detect line ending –
detectLineEnding(content)yields the dominant style (\r\n,\n, or\r). - Normalize user strings – Both
searchandreplaceparameters are processed throughnormalizeLineEndingswith the detected file style. - Perform replacement – The edit logic executes on normalized strings, ensuring matches succeed even with cross-platform input.
- Write back –
writeFilestores the modified content. Because the content derives from the original string (with only the targeted portion changed), the file’s line ending style remains consistent.
Practical Implementation Examples
Detecting a file’s line ending before editing:
import { detectLineEnding } from './utils/lineEndingHandler.js';
import { readFileInternal } from './tools/filesystem.js';
async function checkLineEndings(filePath: string) {
const content = await readFileInternal(filePath, 0, Number.MAX_SAFE_INTEGER);
const style = detectLineEnding(content as string);
console.log(`File uses ${JSON.stringify(style)} line endings`);
}
Preparing edit blocks with normalized line endings:
import { normalizeLineEndings } from './utils/lineEndingHandler.js';
function prepareEdit(
search: string,
replace: string,
fileLineEnding: '\r\n' | '\n' | '\r'
) {
return {
normSearch: normalizeLineEndings(search, fileLineEnding),
normReplace: normalizeLineEndings(replace, fileLineEnding)
};
}
Complete line-ending-aware edit operation:
import { readFileInternal, writeFile } from './tools/filesystem.js';
import { detectLineEnding, normalizeLineEndings } from './utils/lineEndingHandler.js';
async function editFile(
filePath: string,
search: string,
replace: string
) {
const raw = await readFileInternal(filePath, 0, Number.MAX_SAFE_INTEGER) as string;
const lineEnding = detectLineEnding(raw);
const normalizedSearch = normalizeLineEndings(search, lineEnding);
const normalizedReplace = normalizeLineEndings(replace, lineEnding);
const newContent = raw.split(normalizedSearch).join(normalizedReplace);
await writeFile(filePath, newContent);
}
Summary
- Line ending handling in DesktopCommanderMCP prevents cross-platform file corruption by preserving original
\r\n,\n, or\rconventions. - The
detectLineEndingfunction insrc/utils/lineEndingHandler.tsidentifies the dominant style by scanning raw file content. normalizeLineEndingsconverts user input to match the file’s specific line ending format before performing edits.- The edit workflow in
src/tools/edit.tsreads raw content, detects line endings, normalizes search/replace strings, and writes back while maintaining consistency. - This architecture ensures accurate diffs in Claude Desktop, supports mixed-ending files, and optimizes token usage during file streaming.
Frequently Asked Questions
What line ending styles does DesktopCommanderMCP support?
DesktopCommanderMCP supports all three standard line ending sequences: CRLF (\r\n) used by Windows, LF (\n) used by Unix/Linux and macOS, and legacy CR (\r) used by classic Mac OS. The detectLineEnding utility in src/utils/lineEndingHandler.ts recognizes each style and falls back to the host OS default when no line endings are present.
How does DesktopCommanderMCP handle files with mixed line endings?
When a file contains mixed line endings, the detectLineEnding function identifies the dominant style based on frequency. The system then normalizes user input to match this dominant style, ensuring that edits do not introduce additional inconsistency. While the tool preserves the existing mixed state, it prevents further corruption by not converting the entire file to a single style during routine edits.
Why does line ending handling affect diff previews in Claude Desktop?
Claude Desktop generates edit previews by comparing the original file content against the modified version. If line endings change during the edit process, the diff algorithm interprets every line as modified rather than just the intended changes. By preserving original line endings through the normalizeLineEndings workflow, DesktopCommanderMCP ensures that only actual content changes appear in the diff view.
Where is the line ending detection logic located in the codebase?
The core utilities reside in src/utils/lineEndingHandler.ts, which exports detectLineEnding and normalizeLineEndings. The orchestration logic that utilizes these utilities during file edits is implemented in src/tools/edit.ts at approximately line 57, where it coordinates with readFileInternal and writeFile from src/tools/filesystem.ts to execute line-ending-aware modifications.
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 →