Skip to content

File Transfers

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.

ProtocolBlock SizeBatchResumeError DetectionBest For
XMODEM128 bytesNoNoChecksum or CRC-16Legacy compatibility, simple devices
XMODEM-1K1024 bytesNoNoCRC-16Faster XMODEM with wider block size
YMODEM1024 bytesYesNoCRC-16Multiple files, preserves filenames
ZMODEMStreamingYesYesCRC-32Everything 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.

Use file_transfer_send to transmit a file to the remote device.

// 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
}

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.
// 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.

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

// 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.

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.

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

  1. Open the serial port at the bootloader’s baud rate

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

    // 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.

    // 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.

    // 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.

    // 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.

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

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.

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.

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

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

ProtocolRecovery MethodResume Supported
XMODEMRestart from beginningNo
XMODEM-1KRestart from beginningNo
YMODEMRestart from beginningNo
ZMODEMAutomatic resume from interruption pointYes

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:

// 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.

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)

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
// Conservative settings for unreliable links
// configure_serial(port="/dev/ttyUSB0", baudrate=9600, rtscts=true)
ErrorLikely CauseSolution
”Receiver not ready”Receiver not startedStart receiver before sender
Immediate timeoutWrong protocolMatch protocol on both ends
Repeated NAKsLine noise or baud mismatchLower baud rate, check cables
File exists errorOverwrite protectionPass overwrite=true or delete existing
Size mismatchXMODEM paddingUse YMODEM/ZMODEM for exact sizes