# How to Read and Write Excel Files (.xlsx, .xls, .xlsm) Using Desktop Commander MCP

> Learn to read and write Excel files (.xlsx, .xls, .xlsm) directly with Desktop Commander MCP. Process workbooks into JSON arrays without external tools or Microsoft Excel installed. Get started now.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: how-to-guide
- Published: 2026-07-17

---

**Desktop Commander MCP processes Excel files natively through the `ExcelFileHandler` class, using `xlsx-populate` to parse workbooks into two-dimensional JSON arrays without requiring Microsoft Excel or external tools.**

Desktop Commander MCP eliminates the need for external spreadsheet applications when working with Excel formats. The repository's `ExcelFileHandler` class, located in [`src/utils/files/excel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/excel.ts), provides native support for `.xlsx`, `.xls`, and `.xlsm` files by leveraging the `xlsx-populate` library. When you invoke `readFile()` from [`src/tools/filesystem.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/filesystem.ts) on an Excel path, the system automatically detects the extension and routes processing through the specialized handler, returning structured JSON data that LLM tools can consume directly.

## How Desktop Commander MCP Handles Excel Files

The architecture follows a factory pattern to route file operations efficiently. When `readFile()` encounters a path ending in `.xlsx`, `.xls`, or `.xlsm`, the file-handler factory in [`src/utils/files/factory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/factory.ts) returns a singleton instance of `ExcelFileHandler` to process the request.

The handler relies on **xlsx-populate** (declared in [`package.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/package.json)) to parse workbook contents. Instead of returning raw binary data, `ExcelFileHandler.read()` converts the requested cell range into a two-dimensional JSON array wrapped in a `ReadResult` object containing:

- `content` – A `Buffer` containing the JSON string representation of the cell data
- `mimeType` – Set to `application/json` for Excel read operations
- Metadata including the specific sheet name that was processed

## Reading Excel Files with readFile()

To read an Excel file, import the utility from the compiled distribution and invoke `readFile()` with the target path and optional configuration object:

```typescript
import { readFile } from './dist/tools/filesystem.js';

const result = await readFile('/path/to/workbook.xlsx', {
  sheet: 'Employees',
  range: 'A1:C5',
  offset: 1,
  length: 10
});

```

The function returns a `ReadResult` object. Convert the content buffer to a JavaScript array using `JSON.parse()`:

```typescript
const data = JSON.parse(result.content.toString());
console.log(data); // Array of rows/columns

```

### Selecting Sheets and Ranges

The `ExcelFileHandler` supports granular extraction control through four key options passed via the `options` argument:

- **`sheet`** – Target a specific worksheet by name (e.g., `"Employees"`) or zero-based index (e.g., `1`). Defaults to the first sheet if omitted.
- **`range`** – Specify an Excel-style range string such as `"A1:B2"`, a single cell `"C3"`, or a sheet-prefixed range `"Sheet1!A1:B2"`. Supports spaces in sheet names using quoted forms like `"'My Sheet'!A1:B2"`.
- **`offset`** – Number of rows to skip before returning data. Negative values count from the end of the sheet for tail reads.
- **`length`** – Maximum number of rows to return after applying the offset.

These options are validated in the repository's test suite at [`test/test-excel-files.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-excel-files.js).

## Practical Code Examples

### Read an Entire Worksheet

To retrieve all data from the first sheet as a JSON array:

```typescript
const result = await readFile('report.xlsx');
const json = JSON.parse(result.content.toString());
// result.mimeType === 'application/json'

```

### Target a Specific Sheet and Range

Access data from a named sheet with precise cell boundaries:

```typescript
const sales = await readFile('sales.xlsx', {
  sheet: '2024_Q1',
  range: 'A2:D20'
});
const data = JSON.parse(sales.content.toString());

```

### Paginate Large Datasets

Skip header rows and limit results for efficient batch processing:

```typescript
const body = await readFile('log.xlsx', {
  offset: 1,  // Skip header row
  length: 2   // Return only next 2 data rows
});

```

### Handle Sheet Names with Spaces

Use single quotes around sheet names containing spaces:

```typescript
const subset = await readFile('data.xlsx', {
  range: `'My Sheet'!B3:C5`
});

```

## Error Handling and Validation

The `ExcelFileHandler` normalizes error messages to guide troubleshooting. When supplying malformed range strings, the error response includes hints showing the supported `"SheetName!A1:B2"` format, as demonstrated in the "Invalid range must throw" test case within [`test/test-excel-files.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-excel-files.js).

If a specified sheet name does not exist, the handler returns an appropriate error through the factory routing layer.

## Writing and Editing Excel Files

While this guide focuses on read operations, the `ExcelFileHandler` class in [`src/utils/files/excel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/excel.ts) also implements write and edit functionality for creating and modifying `.xlsx`, `.xls`, and `.xlsm` files. The same factory pattern routes write operations through the handler, utilizing `xlsx-populate` to serialize data back to Excel format. Refer to the `ExcelFileHandler` implementation for specific write methods and options.

## Summary

- Desktop Commander MCP routes Excel files (`.xlsx`, `.xls`, `.xlsm`) through the `ExcelFileHandler` class via the factory in [`src/utils/files/factory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/factory.ts).
- The handler uses `xlsx-populate` to parse workbooks without external dependencies like Microsoft Excel.
- `readFile()` returns a `ReadResult` with `application/json` mime type containing a Buffer of JSON array data.
- Control data extraction using `sheet` (name or index), `range` (Excel-style), `offset` (skip rows), and `length` (limit rows) options.
- All read options and edge cases are validated in [`test/test-excel-files.js`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/test/test-excel-files.js).

## Frequently Asked Questions

### Does Desktop Commander MCP require Microsoft Excel to be installed?

No. Desktop Commander MCP handles Excel files natively using the `xlsx-populate` library declared in [`package.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/package.json). The `ExcelFileHandler` class processes all parsing internally through [`src/utils/files/excel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/excel.ts), making external spreadsheet applications unnecessary for reading or writing workbooks.

### What Excel file formats are supported?

The handler supports `.xlsx`, `.xls`, and `.xlsm` extensions. These are automatically detected by the file-handler factory in [`src/utils/files/factory.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/factory.ts), which routes them to the `ExcelFileHandler` singleton for processing.

### How do I handle sheet names that contain spaces?

Use the quoted sheet name format within the range parameter: `"'My Sheet'!A1:B2"`. This syntax is fully supported by the range parser in [`src/utils/files/excel.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/excel.ts) and allows access to worksheets with spaces or special characters in their titles.

### Can I read only specific rows from an Excel file?

Yes. Use the `offset` option to skip rows from the beginning (or use negative values to count from the end), and the `length` option to limit how many rows to return. For example, `{ offset: 1, length: 3 }` skips the first row and returns the next three rows as a JSON array, as demonstrated in the repository's test suite.