---
title: URL Schemes
description: Serial port URL schemes for physical, network, and virtual connections
sidebar:
  order: 5
---

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

The `port` parameter of `open_serial_port` accepts both local device paths and URL schemes. URL schemes let you connect to network serial devices, virtual loopbacks, debug wrappers, and specialized hardware without changing your read/write workflow.

<Aside type="note">
URL-opened ports skip auto-baud detection and exclusive access. The `inter_byte_timeout` and `exclusive` parameters are not passed to URL-based connections.
</Aside>

---

## Device Paths

Standard local serial port paths. These are the most common way to connect to physical hardware.

**Linux:**
```
/dev/ttyUSB0    -- USB-to-serial adapter (FTDI, CP210x, CH340)
/dev/ttyACM0    -- USB CDC ACM device (Arduino, STM32)
/dev/ttyS0      -- Built-in UART (motherboard, Raspberry Pi)
/dev/ttyAMA0    -- ARM UART (Raspberry Pi primary UART)
```

**Windows:**
```
COM3            -- USB-to-serial or built-in COM port
COM12           -- Higher-numbered ports
```

**macOS:**
```
/dev/tty.usbserial-1420  -- USB-to-serial adapter
/dev/tty.usbmodem14201   -- USB CDC ACM device
```

```
open_serial_port(port="/dev/ttyUSB0", baudrate=115200)
```

---

## socket://

Raw TCP socket connection. Connects to serial-to-Ethernet bridges, terminal servers, and networked serial devices that expose a plain TCP port.

**Format:** `socket://host:port`

Data sent to the socket appears on the remote serial port and vice versa. No protocol negotiation occurs -- the connection is a raw byte stream.

**When to use:** Serial-to-Ethernet bridges (Moxa, Digi, Lantronix), ESP32/ESP8266 with TCP server firmware, or any device that bridges serial to a TCP socket.

```
open_serial_port(port="socket://192.168.1.100:4001", baudrate=115200)
```

<Aside type="caution">
The baud rate, parity, and other serial parameters must be configured on the bridge device separately. The `baudrate` parameter here is stored locally but not transmitted to the remote device. Use `rfc2217://` if you need to configure the remote port's serial settings.
</Aside>

---

## rfc2217://

Telnet COM Port Control per RFC 2217. Extends the Telnet protocol to configure remote serial port settings (baud rate, data bits, parity, flow control) over the network.

**Format:** `rfc2217://host:port`

Unlike `socket://`, this scheme negotiates serial parameters with the remote device. When you set `baudrate=115200`, that setting is transmitted to and applied by the remote server.

**When to use:** Remote serial port servers that implement RFC 2217, such as ser2net, or commercial terminal servers with Telnet COM Port support.

```
# Remote serial with full parameter control
open_serial_port(port="rfc2217://192.168.1.100:2217", baudrate=115200)
```

<Aside type="tip">
If your serial server supports RFC 2217, always prefer it over `socket://`. It ensures the remote port is configured with the correct baud rate and framing, rather than relying on out-of-band configuration.
</Aside>

---

## loop://

Loopback virtual port. Everything written is echoed back as readable data. No hardware required.

**Format:** `loop://`

**When to use:** Testing, development, CI/CD pipelines, or verifying tool behavior without physical serial hardware.

```
open_serial_port(port="loop://", baudrate=9600)
write_serial(port="loop://", data="hello")
read_serial(port="loop://")  # Returns "hello"
close_serial_port(port="loop://")
```

---

## spy://

Debug wrapper that captures all serial traffic to an in-memory buffer while passing data through to the underlying port. Wraps a real device path.

**Format:** `spy://device_path`

When you open a port with `spy://`, traffic logging is automatically enabled with a 1000-entry buffer. All bytes read and written are captured and can be retrieved later via the `serial://{port}/log` resource.

**When to use:** Debugging communication issues, reverse-engineering protocols, multi-tasking (send commands, do other work, read traffic log later), or logging traffic for analysis.

```
# Wraps /dev/ttyUSB0 with automatic traffic capture
open_serial_port(port="spy:///dev/ttyUSB0", baudrate=9600)

# Send a command
write_serial(port="spy:///dev/ttyUSB0", data="AT\r\n")

# ... do other work while device responds ...

# Read captured traffic via resource
# serial://spy:///dev/ttyUSB0/log
```

<Aside type="tip">
The spy:// scheme is especially useful for MCP clients. Unlike stderr logging, the captured traffic is accessible via MCP resources, letting the model send commands, perform other tasks, and then review the traffic log later.
</Aside>

<Aside type="note">
Traffic capture stores raw bytes in memory. The default buffer of 1000 entries is suitable for most debugging sessions. For longer sessions or high-throughput connections, consider using `enable_traffic_log()` to set a custom buffer size.
</Aside>

---

## cp2110://

Silicon Labs CP2110 HID-to-UART bridge. Connects to CP2110 devices via the USB HID interface rather than a traditional serial driver.

**Format:** `cp2110://`

**When to use:** Devices using the Silicon Labs CP2110 chip that present as HID devices rather than standard serial ports.

<Aside type="caution" title="Requires extra dependency">
This scheme requires the `hidapi` library. Install it with:

```bash
uv pip install mcserial[cp2110]
```

Without this extra, opening a `cp2110://` URL returns an error explaining the missing dependency.
</Aside>

```
open_serial_port(port="cp2110://", baudrate=9600)
```

---

## hwgrep://

Hardware grep -- opens the first serial port matching a hardware description pattern. Searches across device path, description, hardware ID, manufacturer, product, and serial number fields.

**Format:** `hwgrep://pattern`

**When to use:** When you know the type of device but not the exact port assignment. Useful for scripts that need to find a specific adapter regardless of which USB port it is plugged into.

```
# Open the first FTDI adapter found
open_serial_port(port="hwgrep://FTDI", baudrate=115200)

# Match by USB VID:PID
open_serial_port(port="hwgrep://VID:PID=0403:6001", baudrate=9600)
```

<Aside type="tip">
For port discovery without opening, use `list_serial_ports(grep="FTDI")` to see all matching ports first, then open a specific one by device path.
</Aside>

---

## Feature Comparison

Different URL schemes support different mcserial capabilities. Use this table to choose the right scheme for your needs:

| Scheme | Exclusive Access | Flow Control | Auto-Baud | Modem Lines | RS-485 | Notes |
|--------|-----------------|--------------|-----------|-------------|--------|-------|
| Device path | ✅ | ✅ | ✅ | ✅ | ✅ | Full feature set |
| `socket://` | ❌ | ❌ | ❌ | ❌ | ❌ | Raw TCP only |
| `rfc2217://` | ❌ | ✅ | ❌ | ✅ | ❌ | Via Telnet negotiation |
| `loop://` | ✅ | N/A | N/A | N/A | N/A | Testing only |
| `spy://` | ✅ | ✅ | ✅ | ✅ | ✅ | Wraps underlying device |
| `cp2110://` | ✅ | ❌ | ❌ | ❌ | ❌ | HID interface limitations |
| `hwgrep://` | ✅ | ✅ | ✅ | ✅ | ✅ | Opens matched device |

<Aside type="note" title="URL scheme limitations">
When opening via URL schemes (except `spy://` and `hwgrep://`, which wrap physical devices):

- `inter_byte_timeout` is not passed to URL-based connections
- `exclusive` access cannot be enforced
- Auto-baud detection is skipped
- Some operations may silently fail or return defaults

For full functionality, prefer physical device paths when possible.
</Aside>

---

## Summary

| Scheme | Use Case | Remote Config | Hardware Required |
|--------|----------|---------------|-------------------|
| Device path | Local serial hardware | N/A | Yes |
| `socket://` | Serial-to-Ethernet bridges | No (configure bridge separately) | Network bridge |
| `rfc2217://` | Remote serial with parameter control | Yes (via Telnet negotiation) | RFC 2217 server |
| `loop://` | Testing and development | N/A | None |
| `spy://` | Traffic debugging | N/A | Underlying device |
| `cp2110://` | Silicon Labs HID-to-UART | N/A | CP2110 chip |
| `hwgrep://` | Auto-detect by hardware description | N/A | Matched device |
