Skip to content

Getting Started

mcserial is a FastMCP server that gives MCP clients full serial port access — discovery, configuration, read/write, modem control, and file transfers. You use it through your MCP client (Claude Code, Claude Desktop, or any other MCP-compatible client) by talking to your assistant in natural language. This tutorial walks you through installation, client configuration, and your first serial connection.

  • Python 3.10+ (check with python --version)
  • uv (recommended) or pip for package management
  • A serial device, or nothing at all — loop:// lets you test without hardware
Terminal window
# Run directly -- no install step needed
uvx mcserial

uvx fetches and runs the latest version in an isolated environment. Nothing is installed globally.

mcserial runs over stdio, so you register it with your MCP client and the client launches it on demand.

Terminal window
claude mcp add mcserial -- uvx mcserial

That’s it. Claude Code will start mcserial automatically when serial tools are needed.

The natural workflow is: discover available ports, open one, read/write data, then close when done. You ask the assistant to do each step, and it calls the appropriate mcserial tools.

This walkthrough uses loop:// so you can follow along without any hardware. Everything written to a loopback port is immediately available to read back.

  1. Ask the assistant to list available ports

    The assistant calls list_serial_ports to see what’s connected:

    list_serial_ports(usb_only=false)

    Response:

    [
    {
    "device": "/dev/ttyUSB0",
    "description": "FT232R USB UART",
    "hwid": "USB VID:PID=0403:6001",
    "manufacturer": "FTDI",
    "product": "FT232R USB UART",
    "serial_number": "A50285BI",
    "is_open": false
    }
    ]

    Set usb_only=false to include built-in serial ports. Use grep to filter by hardware: list_serial_ports(grep="FTDI").

  2. Open a connection

    Ask the assistant to open the loopback port (or a real device path from step 1). The assistant calls:

    open_serial_port(port="loop://", baudrate=9600)

    Response:

    {
    "success": true,
    "port": "loop://",
    "mode": "rs232",
    "baudrate": 9600,
    "bytesize": 8,
    "parity": "N",
    "stopbits": 1,
    "xonxoff": false,
    "rtscts": false,
    "dsrdtr": false,
    "resource_uri": "serial://loop:///data",
    "url_scheme": "loop",
    "hint": "Opened via URL handler. Some features (exclusive, auto-baud) are not available."
    }

    The port is now open in RS-232 mode with 8N1 framing (8 data bits, no parity, 1 stop bit) — the most common configuration.

  3. Send a command and read the response

    The fastest way to interact is transact — it writes your command, flushes, and reads the response in a single call:

    transact(port="loop://", data="Hello, serial world!\n")

    Response:

    {
    "success": true,
    "port": "loop://",
    "bytes_sent": 21,
    "data_sent": "Hello, serial world!\n",
    "response": "Hello, serial world!\n",
    "response_bytes": 21,
    "response_hex": "48656c6c6f2c2073657269616c20776f726c64210a"
    }

    Since this is a loopback, the response is exactly what you sent. With a real device, you’d see the device’s response instead.

  4. Set a default line ending (optional)

    Most serial devices expect a line ending after each command (\r\n, \r, or \n). Instead of appending it to every command, set it once:

    configure_serial(port="loop://", line_ending="crlf")

    Now transact(data="AT") automatically sends AT\r\n. This applies to write_serial too.

  5. Close the connection

    When you’re done, ask the assistant to close the port to release the resource:

    close_serial_port(port="loop://")

    Response:

    {
    "success": true,
    "port": "loop://",
    "message": "Port closed"
    }

mcserial provides several ways to interact with serial data, each suited to different situations:

ToolWhen to use
transactSend a command and read the response in one call (preferred for interactive sessions)
read_serialRead whatever bytes are available right now
read_serial_lineRead one line (up to \n)
read_serial_linesDrain multi-line responses (up to max_lines)
read_untilRead until a custom terminator (e.g., > prompt)

When opening a real device, you can omit baudrate and mcserial will attempt to detect it automatically:

open_serial_port(port="/dev/ttyUSB0")

The response includes detection details:

{
"success": true,
"port": "/dev/ttyUSB0",
"baudrate": 115200,
"autobaud": {
"auto_detected": true,
"detection_confidence": 87.3,
"detection_candidates": [
{"baudrate": 115200, "score": 87.3},
{"baudrate": 9600, "score": 12.1}
]
}
}

Auto-detection works best when the device is actively sending data. You can also pass autobaud_probe="UUUUU" to send a sync string that triggers a response.

Loopback testing

Practice read/write patterns, encodings, and flow control without hardware using loop:// virtual ports.

RS-232 basics

Learn modem line control, device resets, and break signals in the RS-232 guide.

RS-485 and Modbus

Set up multi-drop bus communication and device scanning in the RS-485 guide.