Loopback testing
Practice read/write patterns, encodings, and flow control without hardware using loop:// virtual ports.
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 --version)loop:// lets you test without hardware# Run directly -- no install step neededuvx mcserialuvx fetches and runs the latest version in an isolated environment. Nothing is installed globally.
pip install mcserialIf you need CP2110 HID-to-UART support:
pip install mcserial[cp2110]mcserial runs over stdio, so you register it with your MCP client and the client launches it on demand.
claude mcp add mcserial -- uvx mcserialThat’s it. Claude Code will start mcserial automatically when serial tools are needed.
Add this to your claude_desktop_config.json:
{ "mcpServers": { "mcserial": { "command": "uvx", "args": ["mcserial"] } }}On macOS, the config file is at ~/Library/Application Support/Claude/claude_desktop_config.json. On Windows, it’s at %APPDATA%\Claude\claude_desktop_config.json.
Any MCP client that supports stdio transport can run mcserial. The command is:
uvx mcserialPass this as the server command in your client’s MCP configuration. Consult your client’s documentation for the exact format.
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.
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").
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.
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.
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.
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:
| Tool | When to use |
|---|---|
transact | Send a command and read the response in one call (preferred for interactive sessions) |
read_serial | Read whatever bytes are available right now |
read_serial_line | Read one line (up to \n) |
read_serial_lines | Drain multi-line responses (up to max_lines) |
read_until | Read 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.
Tool reference
Full parameter details for all 30 tools in the reference section.