Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,17 @@ Absolutely. While it excels at coding-related tasks, Claude Desktop Commander ca
- Running and managing any terminal-based tools
- Data processing and analysis

### Can I read files from the end like Unix tail?

Yes! Recent updates added negative offset support:

```javascript
// Read last 10 lines
read_file({ path: "server.log", offset: -10 })
```

This is useful for checking recent log entries or file endings without reading the entire content.

### Can I use Desktop Commander in any MCP client outside of Claude?

Yes, you can install Desktop Commander MCP on other clients that support MCP, including:
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Execute long-running terminal commands on your computer and manage processes thr
- Move files/directories
- Search files
- Get file metadata
- **Negative offset file reading**: Read from end of files using negative offset values (like Unix tail)
- Code editing capabilities:
- Surgical text replacements for small changes
- Full file rewrites for major changes
Expand Down Expand Up @@ -187,7 +188,7 @@ The server provides a comprehensive set of tools organized into several categori
| | `list_sessions` | List all active terminal sessions |
| | `list_processes` | List all running processes with detailed information |
| | `kill_process` | Terminate a running process by PID |
| **Filesystem** | `read_file` | Read contents from local filesystem or URLs with line-based pagination (supports offset and length parameters) |
| **Filesystem** | `read_file` | Read contents from local filesystem or URLs with line-based pagination (supports positive/negative offset and length parameters) |
| | `read_multiple_files` | Read multiple files simultaneously |
| | `write_file` | Write file contents with options for rewrite or append mode (uses configurable line limits) |
| | `create_directory` | Create a new directory or ensure it exists |
Expand Down
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@ <h3>Subscription-based usage</h3>
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
</div>
<h3>Smart file system integration</h3>
<p>Claude understands your project structure with intelligent file search and can make precise surgical changes to your codebase.</p>
<p>Claude understands your project structure with intelligent file search and can make precise surgical changes to your codebase. Now supports reading from file end with negative offsets.</p>
</div>

<div class="benefit-card">
Expand Down
6 changes: 2 additions & 4 deletions src/handlers/filesystem-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,9 @@ export async function handleWriteFile(args: unknown): Promise<ServerResult> {
const lineCount = lines.length;
let errorMessage = "";
if (lineCount > MAX_LINES) {
errorMessage = `File was written with warning: Line count limit exceeded: ${lineCount} lines (maximum: ${MAX_LINES}).
errorMessage = `File written successfully! (${lineCount} lines)

SOLUTION: Split your content into smaller chunks:
1. First chunk: write_file(path, firstChunk, {mode: 'rewrite'})
2. Additional chunks: write_file(path, nextChunk, {mode: 'append'})`;
💡 Performance tip: For optimal speed, consider chunking files into ≤30 line pieces in future operations.`;
}

// Pass the mode parameter to writeFile
Expand Down
63 changes: 39 additions & 24 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {

Supports partial file reading with:
- 'offset' (start line, default: 0)
* Positive: Start from line N (0-based indexing)
* Negative: Read last N lines from end (tail behavior)
- 'length' (max lines to read, default: configurable via 'fileReadLineLimit' setting, initially 1000)
* Used with positive offsets for range reading
* Ignored when offset is negative (reads all requested tail lines)

Examples:
- offset: 0, length: 10 → First 10 lines
- offset: 100, length: 5 → Lines 100-104
- offset: -20 → Last 20 lines
- offset: -5, length: 10 → Last 5 lines (length ignored)

Performance optimizations:
- Large files with negative offsets use reverse reading for efficiency
- Large files with deep positive offsets use byte estimation
- Small files use fast readline streaming

When reading from the file system, only works within allowed directories.
Can fetch content from URLs when isUrl parameter is set to true
Expand Down Expand Up @@ -158,30 +173,30 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
{
name: "write_file",
description: `
Write or append to file contents with a configurable line limit per call (default: 50 lines).
THIS IS A STRICT REQUIREMENT. ANY file with more than the configured limit MUST BE written in chunks or IT WILL FAIL.

⚠️ IMPORTANT: PREVENTATIVE CHUNKING REQUIRED in these scenarios:
1. When content exceeds 2,000 words or 30 lines
2. When writing MULTIPLE files one after another (each next file is more likely to be truncated)
3. When the file is the LAST ONE in a series of operations in the same message

ALWAYS split files writes in to multiple smaller writes PREEMPTIVELY without asking the user in these scenarios.
REQUIRED PROCESS FOR LARGE NEW FILE WRITES OR REWRITES:
1. FIRST → write_file(filePath, firstChunk, {mode: 'rewrite'})
2. THEN → write_file(filePath, secondChunk, {mode: 'append'})
3. THEN → write_file(filePath, thirdChunk, {mode: 'append'})
... and so on for each chunk

HANDLING TRUNCATION ("Continue" prompts):
If user asked to "Continue" after unfinished file write:
1. First, read the file to find out what content was successfully written
2. Identify exactly where the content was truncated
3. Continue writing ONLY the remaining content using {mode: 'append'}
4. Split the remaining content into smaller chunks (15-20 lines per chunk)

Files over the line limit (configurable via 'fileWriteLineLimit' setting) WILL BE REJECTED if not broken into chunks as described above.
Write or append to file contents.

🎯 CHUNKING IS STANDARD PRACTICE: Always write files in chunks of 25-30 lines maximum.
This is the normal, recommended way to write files - not an emergency measure.

STANDARD PROCESS FOR ANY FILE:
1. FIRST → write_file(filePath, firstChunk, {mode: 'rewrite'}) [≤30 lines]
2. THEN → write_file(filePath, secondChunk, {mode: 'append'}) [≤30 lines]
3. CONTINUE → write_file(filePath, nextChunk, {mode: 'append'}) [≤30 lines]

⚠️ ALWAYS CHUNK PROACTIVELY - don't wait for performance warnings!

WHEN TO CHUNK (always be proactive):
1. Any file expected to be longer than 25-30 lines
2. When writing multiple files in sequence
3. When creating documentation, code files, or configuration files

HANDLING CONTINUATION ("Continue" prompts):
If user asks to "Continue" after an incomplete operation:
1. Read the file to see what was successfully written
2. Continue writing ONLY the remaining content using {mode: 'append'}
3. Keep chunks to 25-30 lines each

Files over 50 lines will generate performance notes but are still written successfully.
Only works within allowed directories.

${PATH_GUIDANCE}
Expand Down
6 changes: 3 additions & 3 deletions src/tools/edit.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readFile, writeFile } from './filesystem.js';
import { readFile, writeFile, readFileInternal } from './filesystem.js';
import { ServerResult } from '../types.js';
import { recursiveFuzzyIndexOf, getSimilarityRatio } from './fuzzySearch.js';
import { capture } from '../utils/capture.js';
Expand Down Expand Up @@ -119,8 +119,8 @@ export async function performSearchReplace(filePath: string, block: SearchReplac
}


// Read file as plain string
const {content} = await readFile(filePath, false, 0, Number.MAX_SAFE_INTEGER);
// Read file as plain string without status messages
const content = await readFileInternal(filePath, 0, Number.MAX_SAFE_INTEGER);

// Make sure content is a string
if (typeof content !== 'string') {
Expand Down
Loading