From a28eed3849e4ccfe9fd8511341e34628600a8e59 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Fri, 30 Jan 2026 20:49:35 -0700 Subject: [PATCH] Rewrite README with real-world example and project structure --- README.md | 92 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index ca4c8f7..f941402 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,37 @@ # mcp210x -MCP server for customizing Silicon Labs CP210x USB-UART bridge devices. Read and write USB descriptor strings, power configuration, and device lock state — all through natural language in Claude Code. +It's MCP. It's CP210x. It was right there the whole time. -Built on [FastMCP](https://gofastmcp.com/) and Silicon Labs' `libcp210xmanufacturing` native library via Python ctypes. +An MCP server for customizing Silicon Labs CP210x USB-UART bridge devices — product strings, serial numbers, power config, udev rules, and device locking — through natural language in Claude Code. -## Why +Built on [FastMCP](https://gofastmcp.com/) with Python ctypes bindings to Silicon Labs' native `libcp210xmanufacturing` library. -CP210x chips (CP2102, CP2102N, CP2104, etc.) store USB descriptor strings in one-time-programmable EPROM. Silicon Labs provides a GUI tool and a C library for customization, but both are clunky to use — especially when managing multiple identical devices that need unique identifiers. +## The problem -This MCP server lets you customize devices conversationally: +You plug in three CP2102 boards. They all enumerate as: + +``` +Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP2102 USB to UART Bridge Controller +Bus 001 Device 005: ID 10c4:ea60 Silicon Labs CP2102 USB to UART Bridge Controller +Bus 001 Device 006: ID 10c4:ea60 Silicon Labs CP2102 USB to UART Bridge Controller +``` + +Which one is which? Unplug, replug, guess. `/dev/ttyUSB0` becomes `/dev/ttyUSB2` after a reboot. You write udev rules that match on nothing unique. The Silicon Labs GUI customization tool is 32-bit only and hasn't been updated since 2015. + +## The fix ``` > What CP210x devices are connected? -> Change the product string of device 0 to "RYLR998 0033001104645C0B00001130" -> Set up a udev rule so it always appears at /dev/rylr998-1130 + +Two devices found: + [0] RYLR998 0033001104645C0B00001130 (serial: 0001) + [1] RYLR998 0033001104645C0B00000D27 (serial: 0001) + +> Set up a udev rule for device 0 so it always appears at /dev/rylr998-1130 ``` +Each device gets a unique product string baked into its USB descriptor EPROM. Udev rules match on that string to create stable symlinks. Devices survive reboots, port reordering, and hub changes. + ## Features - **List and inspect** connected CP210x devices (part number, VID/PID, strings, power, lock state) @@ -23,55 +39,67 @@ This MCP server lets you customize devices conversationally: - **Configure power** — max current draw, self-powered vs bus-powered - **Generate udev rules** — stable `/dev/` symlinks based on product string - **Reset device** — trigger USB re-enumeration after changes -- **Lock device** — permanently freeze configuration (with strict confirmation) -- **Elicitation support** — write operations request confirmation when the MCP client supports it; graceful fallback otherwise. Lock is the exception: it *requires* explicit confirmation and will refuse if elicitation is unavailable. +- **Lock device** — permanently freeze configuration (with strict confirmation gate) + +## Safety model + +CP210x descriptor EPROM is one-time-programmable with limited write cycles. Writes can't be undone. Locks are permanent. The server enforces a tiered confirmation model: + +| Operation | Confirmation | +|-----------|-------------| +| Reads | None | +| Writes (strings, power) | MCP elicitation if client supports it; proceeds otherwise | +| Lock | Elicitation **required**; hard-refuses without it | + +The lock gate isn't just a warning — it returns an error and does not proceed if the MCP client can't present a confirmation dialog. ## Requirements - Linux x86_64 - `libcp210xmanufacturing.so` — Silicon Labs CP210x manufacturing library - Python 3.10+ -- [uv](https://docs.astral.sh/uv/) (recommended) +- [uv](https://docs.astral.sh/uv/) ## Installation -### 1. Install the native library +### 1. Native library -**Arch Linux (AUR package included):** +**Arch Linux** (AUR package included): ```bash cd aur/cp210xmanufacturing makepkg -si ``` +This installs the shared library, headers, and udev rules for non-root USB access. + **From source:** ```bash -# Extract AN721SW toolkit (included in docs/) cd AN721SW/Linux/LibrarySourcePackages/cp210xmanufacturing make LIB_ARCH=64 sudo make install sudo ldconfig ``` -The library installs to `/usr/lib/libcp210xmanufacturing.so` and uses libusb to communicate with devices. You'll need udev rules for non-root access — the AUR package installs these automatically, or copy `aur/cp210xmanufacturing/SiliconLabs.rules` to `/usr/lib/udev/rules.d/`. +You'll also need udev rules for non-root device access — copy `aur/cp210xmanufacturing/SiliconLabs.rules` to `/usr/lib/udev/rules.d/` and reload. -### 2. Install the MCP server +### 2. MCP server ```bash uv tool install . ``` -### 3. Add to Claude Code +### 3. Claude Code ```bash claude mcp add cp210x -- uvx mcp210x ``` -Or for development (runs from source): +For development (runs from source): ```bash -claude mcp add cp210x-local -- uv run --directory /path/to/cp2102-uart mcp210x +claude mcp add cp210x-local -- uv run --directory /path/to/this-repo mcp210x ``` ## Tools @@ -83,10 +111,10 @@ claude mcp add cp210x-local -- uv run --directory /path/to/cp2102-uart mcp210x | `set_product_string` | Write USB product string (max 126 chars) | | `set_manufacturer_string` | Write USB manufacturer string (max 45 chars) | | `set_serial_number` | Write USB serial number (max 63 chars) | -| `set_max_power` | Set max USB power draw in mA (0–500, rounded to nearest 2) | +| `set_max_power` | Set max USB power draw in mA (0-500, rounded to nearest 2) | | `set_self_powered` | Toggle self-powered vs bus-powered reporting | | `reset_device` | USB disconnect/reconnect to apply changes | -| `lock_device` | **Permanently** freeze device configuration | +| `lock_device` | Permanently freeze device configuration | | `setup_udev_rule` | Generate and install a udev rule for a stable `/dev/` symlink | ## Architecture @@ -104,19 +132,27 @@ Claude Code ──stdio──▶ FastMCP server (server.py) libusb ──▶ CP210x device ``` -The native library uses **libusb** for device access, which is separate from the kernel's `cp210x` serial driver. Both can coexist — you can read/write UART data over `/dev/ttyUSB0` while simultaneously customizing USB descriptors through this server. +The native library uses **libusb** for device access, separate from the kernel's `cp210x` serial driver. Both coexist — you can read/write UART data over `/dev/ttyUSB0` while customizing USB descriptors through this server. -### Safety model +## Project structure -CP210x EPROM writes are one-time-programmable with limited write cycles. The server uses a tiered confirmation model: - -- **Reads** — no confirmation needed -- **Writes** (strings, power) — requests confirmation via MCP elicitation; falls back to proceeding if the client doesn't support it -- **Lock** — *always* requires elicitation; hard-refuses without it +``` +mcp210x/ +├── src/mcp210x/ +│ ├── server.py # FastMCP tool definitions and elicitation logic +│ ├── bindings.py # ctypes wrapper for libcp210xmanufacturing.so +│ └── __init__.py +├── aur/cp210xmanufacturing/ +│ ├── PKGBUILD # Arch Linux package for the native library +│ └── SiliconLabs.rules # udev rules for non-root USB access +├── AN721SW/ # Silicon Labs toolkit (library source) +├── docs/ # Datasheets and application notes +└── pyproject.toml +``` ## Complementary tools -This server handles **device customization** (USB descriptors, power config). For **serial communication** (sending/receiving data over UART), use [mcserial](https://github.com/ryanmalloy/mcserial). +This server handles **device customization** (USB descriptors, power config). For **serial communication** (sending/receiving data over UART), use [mcserial](https://git.supported.systems/MCP/mcserial). ## Reference