---
name: xls
displayName: Legacy Excel (.xls) Reader & Converter
description: Read and convert legacy Excel OLE/CFB binary (.xls) files to CSV or
  JSON. Use when opening pre-2007 .xls files that cannot be read as plain
  ZIP/XML.
tags:
  - excel
  - xls
  - spreadsheet
  - csv
  - conversion
  - legacy
capabilities:
  - ReadXls
  - ConvertToCsv
  - ConvertToJson
  - ListSheets
  - ExtractSheet
representativeQueries:
  - Read a .xls file and show me its contents
  - Convert an xls spreadsheet to CSV
  - Extract data from a legacy Excel file
  - What sheets are in this .xls file?
  - Export xls to JSON
version: 0.1.0
tier: curated
---

# Legacy Excel (.xls) Reader & Converter

Reads and converts legacy Excel OLE/CFB binary files (`.xls`, Excel 97–2003 format) using the `xlrd` library. Outputs sheet contents as CSV or JSON to stdout. This is a `document` primitive: it detects the binary format and routes through a bundled conversion script.

## When to use

Use this skill when:
- You have a `.xls` file (not `.xlsx`) that needs to be read or exported.
- A downstream tool or pipeline requires CSV/JSON input from a legacy spreadsheet.
- You need to inspect sheet names or extract a specific sheet from a multi-sheet workbook.

Do NOT use for `.xlsx`, `.xlsm`, or `.ods` files — those use different formats and parsers.

## Steps

1. **Detect format.** Confirm the file has a `.xls` extension and is not a ZIP archive (run `file <path>` or check magic bytes `D0 CF 11 E0`). If it is a ZIP, it is `.xlsx` — use a different skill.
2. **Ensure library.** Run `python3 scripts/xls_to_csv.py --check` or attempt import; if `xlrd` is missing, install with `pip install xlrd==1.2.0`.
3. **List sheets (optional).** Run `python3 scripts/xls_to_csv.py <file.xls> --list-sheets` to enumerate sheet names and indices.
4. **Convert.** Run `python3 scripts/xls_to_csv.py <file.xls>` (all sheets, CSV) or pass `--sheet <name|index>` for one sheet, `--format json` for JSON output.
5. **Inspect output.** The script writes to stdout; redirect to a file if needed.

## Operations

| Capability | CRUD | Resource | Tool |
|---|---|---|---|
| `ReadXls` | READ | .xls workbook | `scripts/xls_to_csv.py` |
| `ConvertToCsv` | READ | sheet data | `scripts/xls_to_csv.py` |
| `ConvertToJson` | READ | sheet data | `scripts/xls_to_csv.py` |
| `ListSheets` | READ | sheet index | `scripts/xls_to_csv.py --list-sheets` |
| `ExtractSheet` | READ | single sheet | `scripts/xls_to_csv.py --sheet <name>` |

## Output

- **CSV mode (default):** RFC 4180 CSV to stdout, one section per sheet (preceded by a `# Sheet: <name>` comment line when multiple sheets are present; hidden sheets are tagged `# Sheet: <name> [hidden]` or `[very-hidden]`).
- **JSON mode (`--format json`):** JSON array of objects `[{"sheet": "<name>", "rows": [[...], ...]}]` to stdout; hidden sheets include `"hidden": true` (or `"hidden": "very-hidden"`).
- **Sheet list (`--list-sheets`):** Plain text, one `<index>: <name>` per line; hidden sheets are tagged `[hidden]` or `[very-hidden]`.

## Notes

- `xlrd==1.2.0` is the recommended pinned version. xlrd 2.x dropped `.xlsx` support but still reads `.xls`; pinning 1.2.0 avoids any API surface differences.
- Date cells are emitted as ISO-8601 strings (`YYYY-MM-DDTHH:MM:SS`, or `YYYY-MM-DDTHH:MM:SS.ffffff` when sub-second precision is present in the stored float).
- Empty cells emit an empty string in CSV; `null` in JSON.
- Error cells (e.g. `#N/A`) emit their string representation.
- For password-protected files, xlrd will raise an error — there is no workaround without the password.
- For files with merged cells, only the top-left cell of each merge range carries a value; the rest emit empty.

<!-- runner-fallback -->
## Remote runner (MCP)
Can't run this locally (no setup, missing dependency)? The StealthStack runner exposes the **same code** as server-side MCP tools — no local install needed: `xls_list_sheets`, `xls_to_csv`, `xls_to_json`. Call the `application/mcp` catalog twin of this skill (its `runnerTwin`).
