---
title: File Transfers
description: Sending and receiving files over serial using X/Y/ZMODEM protocols
---

import { Steps, Aside } from '@astrojs/starlight/components';

Serial file transfers are useful when you need to move files to or from devices that lack network connectivity: embedded systems running a bootloader, legacy industrial equipment, air-gapped machines, or remote hardware accessed through a serial console.

All of the operations described in this guide are performed through your MCP client. Ask the assistant to send or receive files, and it will call the appropriate mcserial transfer tools on your behalf. The tool call notation below shows exactly what the assistant invokes.

mcserial implements four protocol variants, all built in without external dependencies.

## Protocol comparison

| Protocol | Block Size | Batch | Resume | Error Detection | Best For |
|----------|-----------|-------|--------|-----------------|----------|
| XMODEM | 128 bytes | No | No | Checksum or CRC-16 | Legacy compatibility, simple devices |
| XMODEM-1K | 1024 bytes | No | No | CRC-16 | Faster XMODEM with wider block size |
| YMODEM | 1024 bytes | Yes | No | CRC-16 | Multiple files, preserves filenames |
| ZMODEM | Streaming | Yes | Yes | CRC-32 | Everything else (recommended) |

**ZMODEM** is the recommended protocol in nearly all cases. It streams data without waiting for per-block acknowledgment, supports batch transfers, can resume interrupted transfers, and uses 32-bit CRC for reliable error detection.

Use XMODEM only when the remote device does not support anything newer -- some bootloaders and legacy equipment only speak XMODEM.

## Sending a file

Use `file_transfer_send` to transmit a file to the remote device.

```json
// file_transfer_send(
//   port="/dev/ttyUSB0",
//   file_path="/home/user/firmware.bin",
//   protocol="zmodem"
// )
{
  "success": true,
  "protocol": "zmodem",
  "file": "/home/user/firmware.bin",
  "bytes_sent": 65536,
  "blocks_sent": 64
}
```

<Aside type="note" title="Receiver must be ready first">
The remote device must be in receive mode before you call `file_transfer_send`. For ZMODEM, many receivers auto-start when they detect the initialization sequence, but XMODEM and YMODEM receivers must be explicitly started.

If you are working with a device that has a command-line interface, send the receive command first (e.g., `rz` for ZMODEM on Linux), then call `file_transfer_send`.
</Aside>

## Receiving a file

Use `file_transfer_receive` to download a file from the remote device.

The meaning of `save_path` depends on the protocol:

- **XMODEM**: `save_path` is the full file path (e.g., `/tmp/received.bin`). XMODEM does not transmit filenames.
- **YMODEM / ZMODEM**: `save_path` is a directory. The filename comes from the sender's metadata.

```json
// Receiving with XMODEM (save_path is a file path)
// file_transfer_receive(
//   port="/dev/ttyUSB0",
//   save_path="/tmp/received.bin",
//   protocol="xmodem"
// )
{
  "success": true,
  "protocol": "xmodem",
  "file": "/tmp/received.bin",
  "bytes_received": 8192
}

// Receiving with ZMODEM (save_path is a directory)
// file_transfer_receive(
//   port="/dev/ttyUSB0",
//   save_path="/tmp/downloads/",
//   protocol="zmodem"
// )
{
  "success": true,
  "protocol": "zmodem",
  "files_received": 1,
  "bytes_received": 65536
}
```

Set `overwrite=true` if you want to replace existing files at the destination. By default, the transfer fails if a file already exists.

## Batch transfers

YMODEM and ZMODEM support sending multiple files in a single session. Use `file_transfer_send_batch`:

```json
// file_transfer_send_batch(
//   port="/dev/ttyUSB0",
//   file_paths=[
//     "/home/user/firmware.bin",
//     "/home/user/config.json",
//     "/home/user/certs/device.pem"
//   ],
//   protocol="zmodem"
// )
{
  "success": true,
  "protocol": "zmodem",
  "files_sent": 3,
  "total_bytes": 98304
}
```

XMODEM does not support batch transfers -- it has no concept of filenames or session boundaries. If you try to use `file_transfer_send_batch` with XMODEM, you will get an error.

## Security

mcserial validates all file paths during transfers:

- **Directory traversal prevention**: Filenames received from the remote side are sanitized. Paths containing `..` or absolute path components are rejected.
- **Overwrite protection**: By default, receiving a file that already exists on disk returns an error. Pass `overwrite=true` to allow replacement.
- **Parent directory creation**: When receiving, mcserial creates parent directories as needed using `mkdir -p` behavior.

## Practical example: uploading firmware

This example walks through uploading a firmware binary to an embedded device over its serial debug port.

<Steps>

1. **Open the serial port at the bootloader's baud rate**

   Most bootloaders use 115200 baud. Check your device's documentation.

   ```json
   // open_serial_port(port="/dev/ttyUSB0", baudrate=115200)
   {
     "success": true,
     "port": "/dev/ttyUSB0",
     "baudrate": 115200
   }
   ```

2. **Reset the device into bootloader mode**

   For an Arduino-style device, pulse DTR. For other devices, you may need to hold a specific button or send a command.

   ```json
   // pulse_line(port="/dev/ttyUSB0", line="dtr", duration_ms=100, active_low=true)
   ```

3. **Wait for the bootloader prompt**

   Read lines until you see the bootloader's ready message.

   ```json
   // read_serial_lines(port="/dev/ttyUSB0", max_lines=10)
   {
     "success": true,
     "lines": [
       "Bootloader v2.1",
       "Waiting for XMODEM transfer..."
     ],
     "count": 2
   }
   ```

4. **Send the firmware file**

   Match the protocol to what the bootloader expects. Many embedded bootloaders support XMODEM; more capable ones may support YMODEM or ZMODEM.

   ```json
   // file_transfer_send(
   //   port="/dev/ttyUSB0",
   //   file_path="/home/user/build/firmware.bin",
   //   protocol="xmodem"
   // )
   {
     "success": true,
     "protocol": "xmodem",
     "file": "/home/user/build/firmware.bin",
     "bytes_sent": 32768,
     "blocks_sent": 256
   }
   ```

5. **Verify the device booted with the new firmware**

   After the transfer completes, the bootloader typically flashes the firmware and reboots. Read the startup output to confirm.

   ```json
   // read_serial_lines(port="/dev/ttyUSB0", max_lines=10)
   {
     "success": true,
     "lines": [
       "Firmware v3.0.1 loaded",
       "CRC OK",
       "Booting..."
     ],
     "count": 3
   }
   ```

</Steps>

## Protocol details

### XMODEM

The oldest and simplest protocol (1977). Data is sent in 128-byte blocks with a 1-byte checksum or 16-bit CRC. The receiver must acknowledge each block before the next is sent, which makes it slow on high-latency connections.

**XMODEM-1K** is identical except it uses 1024-byte blocks, reducing overhead.

### YMODEM

An extension of XMODEM that adds batch capability. The first block of each file contains the filename and size as metadata. After all files are sent, a null filename block signals the end of the batch.

### ZMODEM

A streaming protocol that does not wait for per-block acknowledgments. It sends data continuously and only pauses if the receiver reports an error. Features include:

- **Auto-start**: receivers detect the ZMODEM init sequence and begin automatically
- **Resume**: interrupted transfers can pick up where they left off
- **CRC-32**: stronger error detection than XMODEM/YMODEM's CRC-16
- **Variable block size**: adapts to line quality

---

## Error Recovery

### Interrupted Transfers

Transfers can fail due to cable disconnection, power loss, or communication errors. Each protocol handles recovery differently:

| Protocol | Recovery Method | Resume Supported |
|----------|----------------|------------------|
| XMODEM | Restart from beginning | No |
| XMODEM-1K | Restart from beginning | No |
| YMODEM | Restart from beginning | No |
| ZMODEM | Automatic resume from interruption point | Yes |

**ZMODEM resume:** When a ZMODEM transfer is interrupted and restarted, the receiver compares the incoming file header with any partial file on disk. If the file matches (same name, size, and modification time), it requests the sender to skip already-received data:

```json
// Transfer interrupted at 50% — just retry
// file_transfer_send(
//   port="/dev/ttyUSB0",
//   file_path="/home/user/large_firmware.bin",
//   protocol="zmodem"
// )
{
  "success": true,
  "protocol": "zmodem",
  "bytes_sent": 32768,
  "resumed_at": 32768,
  "total_size": 65536
}
```

For XMODEM and YMODEM, you must delete the partial file on the receiver and restart the entire transfer.

### Detecting Partial Transfers

ZMODEM and YMODEM include file size metadata in their headers. XMODEM does not — it pads the final block to 128 or 1024 bytes, so the received file may be slightly larger than the original.

**Verify transfer integrity:**

1. **Compare file sizes** — YMODEM/ZMODEM receivers know the expected size
2. **Check CRC/hash** — calculate a checksum on both ends
3. **Look for padding** — XMODEM files end with padding bytes (typically 0x1A or 0x00)

### Block Corruption

All protocols detect corruption via checksum or CRC. When corruption is detected:

1. **Receiver sends NAK** — requests retransmission
2. **Sender resends block** — the same block, not the entire file
3. **Retry limit** — after several failed attempts, the transfer aborts

Excessive retries indicate:

- **Electrical noise** — use shielded cables, shorter runs
- **Baud rate too high** — reduce speed for better reliability
- **Flow control mismatch** — enable RTS/CTS if available

```json
// Conservative settings for unreliable links
// configure_serial(port="/dev/ttyUSB0", baudrate=9600, rtscts=true)
```

### Common Transfer Errors

| Error | Likely Cause | Solution |
|-------|--------------|----------|
| "Receiver not ready" | Receiver not started | Start receiver before sender |
| Immediate timeout | Wrong protocol | Match protocol on both ends |
| Repeated NAKs | Line noise or baud mismatch | Lower baud rate, check cables |
| File exists error | Overwrite protection | Pass `overwrite=true` or delete existing |
| Size mismatch | XMODEM padding | Use YMODEM/ZMODEM for exact sizes |

<Aside type="tip" title="When in doubt, use ZMODEM">
ZMODEM handles almost every edge case gracefully: resume, corruption recovery, batch transfers, and accurate file sizes. Only fall back to XMODEM when the receiver doesn't support anything newer.
</Aside>
