---
title: Getting Started
description: Install mcserial and make your first serial connection.
---

import { Steps, Tabs, TabItem, Aside, Card, CardGrid } from '@astrojs/starlight/components';

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.

<Aside type="note" title="How to read tool calls in this guide">
  The `tool_name(param=value)` blocks below show what the assistant executes behind the scenes. You don't type these yourself -- instead, ask the assistant in plain English, like "list my serial ports" or "open /dev/ttyUSB0 at 115200 baud." The assistant translates your request into the appropriate tool call.
</Aside>

## Prerequisites

- **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

## Install mcserial

<Tabs>
  <TabItem label="uvx (recommended)">
    ```bash
    # Run directly -- no install step needed
    uvx mcserial
    ```

    `uvx` fetches and runs the latest version in an isolated environment. Nothing is installed globally.
  </TabItem>
  <TabItem label="pip">
    ```bash
    pip install mcserial
    ```

    If you need CP2110 HID-to-UART support:

    ```bash
    pip install mcserial[cp2110]
    ```
  </TabItem>
</Tabs>

## Add to your MCP client

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

<Tabs>
  <TabItem label="Claude Code">
    ```bash
    claude mcp add mcserial -- uvx mcserial
    ```

    That's it. Claude Code will start mcserial automatically when serial tools are needed.
  </TabItem>
  <TabItem label="Claude Desktop">
    Add this to your `claude_desktop_config.json`:

    ```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`.
  </TabItem>
  <TabItem label="Other MCP clients">
    Any MCP client that supports stdio transport can run mcserial. The command is:

    ```bash
    uvx mcserial
    ```

    Pass this as the server command in your client's MCP configuration. Consult your client's documentation for the exact format.
  </TabItem>
</Tabs>

<Aside type="tip">
  You can tune defaults with environment variables before the server starts. Set `MCSERIAL_DEFAULT_BAUDRATE` (default: 9600), `MCSERIAL_DEFAULT_TIMEOUT` (default: 1.0s), or `MCSERIAL_MAX_CONNECTIONS` (default: 10). See the [Environment reference](/reference/environment/) for details.
</Aside>

## Your first connection

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.

<Steps>

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:

   ```json
   [
     {
       "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")`.

   <Aside type="note">
     If you have no hardware, skip this step and go straight to opening `loop://` in the next step.
   </Aside>

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:

   ```json
   {
     "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. **Write data**

   Tell the assistant to send a message. It calls:

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

   Response:

   ```json
   {
     "success": true,
     "bytes_written": 21,
     "port": "loop://"
   }
   ```

4. **Read data back**

   Ask the assistant to read from the port. It reads whatever is waiting in the receive buffer:

   ```
   read_serial(port="loop://")
   ```

   Response:

   ```json
   {
     "success": true,
     "data": "Hello, serial world!\n",
     "bytes_read": 21,
     "raw_hex": "48656c6c6f2c2073657269616c20776f726c64210a",
     "port": "loop://"
   }
   ```

   Since this is a loopback, you get back exactly what you wrote. With a real device, you'd see the device's response instead.

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:

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

</Steps>

## Reading tools at a glance

mcserial provides several ways to read data, each suited to different situations:

| Tool | When to use |
|------|-------------|
| `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) |

## Auto-baud detection

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:

```json
{
  "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.

<Aside type="caution">
  Auto-baud detection is only available for local device paths. URL-based ports (`loop://`, `socket://`, etc.) skip detection and fall back to the default baud rate.
</Aside>

## What's next

<CardGrid>
  <Card title="Loopback testing" icon="rocket">
    Practice read/write patterns, encodings, and flow control without hardware using [loop:// virtual ports](/tutorials/loopback-testing/).
  </Card>
  <Card title="RS-232 basics" icon="setting">
    Learn modem line control, device resets, and break signals in the [RS-232 guide](/guides/rs232-basics/).
  </Card>
  <Card title="RS-485 and Modbus" icon="random">
    Set up multi-drop bus communication and device scanning in the [RS-485 guide](/guides/rs485-modbus/).
  </Card>
  <Card title="Tool reference" icon="open-book">
    Full parameter details for all 30 tools in the [reference section](/reference/tools-common/).
  </Card>
</CardGrid>
