cynthion-mcp
Drive a Cynthion USB test instrument from an LLM via the Model Context Protocol. Sniff, decode, and emulate USB devices over chat.
README
cynthion-mcp
Drive a Cynthion USB test instrument from an LLM via the Model Context Protocol. Sniff, decode, and emulate USB devices over chat.
Cynthion is an open-source USB test instrument built around an ECP5 FPGA. It can passively capture Low/Full/High-speed USB traffic, or impersonate a USB device on its TARGET port. This project exposes those capabilities to LLMs (Claude Desktop, Claude Code, any MCP-aware client) as a set of stdio tools so the model can perform end-to-end USB reverse engineering workflows.
What it does
┌─ switch_mode('analyzer') ──────┐
│ capture_start │
plug in target ───┤ (target enumerates) │
│ capture_stop │
└─ convert_to_pcap ───────────────┘
│
▼
┌─ transaction_summary ──────────┐
│ dissect_packets(filter, …) │ ← LLM analyses,
│ find_vendor_requests │ extracts descriptors,
└────────────────────────────────┘ spots protocol patterns
│
▼
┌─ switch_mode('facedancer') ────┐
│ emulator_diagnose │
│ emulate_from_descriptor(…) │ ← clone the device,
│ emulate_device('ftdi') etc │ fuzz responses,
│ disconnect_device │ replay vendor reqs
└────────────────────────────────┘
Three capability groups; the FPGA can only run one bitstream at a time and switch_mode flips between them transparently.
| Mode | Bitstream | What the LLM can do |
|---|---|---|
| Sniffer | analyzer.bit |
Passively capture USB traffic on TARGET-C ↔ TARGET-A |
| Decoder | (host-side) | Turn captures into structured per-packet records via tshark |
| Emulator | facedancer.bit |
Impersonate a USB device on TARGET-C (clone, fuzz, MITM) |
Prerequisites
- A Cynthion USB test instrument (any r0.x or r1.x).
- macOS / Linux. (Windows likely works but is untested.)
- Python ≥ 3.10.
tsharkon$PATH(the decoder tools shell out to it).- macOS:
brew install wireshark - Linux:
apt install tshark/dnf install wireshark-cli
- macOS:
- A working
luna+cynthionPython install. The simplest setup is two source clones sharing a venv (see Setup below).
Setup
# 1. Clone the dependencies and this project side-by-side
git clone https://github.com/greatscottgadgets/luna.git
git clone https://github.com/greatscottgadgets/cynthion.git
git clone https://github.com/Oliver0804/cynthion-mcp.git
# 2. Create a venv and install everything into it
python3 -m venv .venv
./.venv/bin/pip install -e ./luna
./.venv/bin/pip install -e ./cynthion/cynthion/python
./.venv/bin/pip install -e ./cynthion-mcp
# 3. Copy Cynthion's prebuilt bitstreams + Moondancer firmware into the source tree.
# Without these, `cynthion run …` can't load the FPGA. They ship inside the
# PyPI wheel; the source clone doesn't include them.
./.venv/bin/pip download cynthion --no-deps -d /tmp/cynthion-wheel
unzip /tmp/cynthion-wheel/cynthion-*.whl 'cynthion/assets/*' -d /tmp/cynthion-extracted
cp -r /tmp/cynthion-extracted/cynthion/assets/* ./cynthion/cynthion/python/assets/
# 4. Verify the board is reachable
./.venv/bin/cynthion info
⚠️
facedancer==3.1.1is pinned. facedancer 3.1.2 changed the libgreat-RPC protocol that the Moondancer SoC firmware shipped incynthion 0.2.4speaks. The emulator tools will hang onconnect()if you upgrade. Don't bumpfacedancerunlesscynthionalso ships a new firmware.
Register with Claude Code
claude mcp add -s user cynthion /absolute/path/to/.venv/bin/cynthion-mcp
Restart Claude Code (/exit and reopen). Seventeen tools appear under the mcp__cynthion__* namespace.
Register with Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"cynthion": {
"command": "/absolute/path/to/.venv/bin/cynthion-mcp"
}
}
}
Restart Claude Desktop.
Tool reference
Hardware (3)
get_status()— current bitstream + USB enumeration state.switch_mode(applet)—"analyzer"/"facedancer"/"selftest". Handles JTAG-stuck recovery via Apollosoft_reset.recover()— software unstick for handoff timeouts / USB glitches.
Sniffer (5)
capture_start(speed)—"auto"(HS/FS/LS auto-detect on r0.6+),"high","full","low".capture_stop()— returns byte count + duration.capture_status()— peek at running capture's progress.list_captures()— enumerate stored captures (~/.cynthion-mcp/captures/).read_capture(capture_id, offset, length)— slice raw bytes as hex (debugging mainly).
Decoder (4) — tshark-backed
convert_to_pcap(capture_id)— Cynthion native.bin→LINKTYPE_USB_2_0pcap. Idempotent.dissect_packets(capture_id, display_filter, limit)— structured per-packet records.display_filteraccepts full Wireshark display-filter syntax (usbll.pid == 0x96,usbll.device_addr == 16, etc).transaction_summary(capture_id)— PID counts + device address counts at a glance.find_vendor_requests(capture_id, limit)— preset filter for vendor-class SETUP packets — high-value targets when reversing proprietary protocols.
Emulator (5) — Facedancer
emulator_diagnose()— probe the Moondancer SoC's libgreat-RPC. Always call before other emulator tools.emulate_device(device_type, vendor_id?, product_id?)— built-in templates:"ftdi"/"keyboard"⚠️ /"vendor". The keyboard flavour injects keystrokes — only use intentionally.emulate_from_descriptor(device_descriptor_hex, configuration_descriptor_hex?, strings?)— device cloning: stand up an emulation built from raw descriptor bytes you pulled out of a capture. The closed-loop pairing withdissect_packets.disconnect_device()— stop active emulation.inject_serial(text)— push UTF-8 out an active FTDI emulation's bulk-IN endpoint.
Example session
A round-trip "clone an unknown USB device" prompt for Claude:
1. switch to analyzer mode
2. start a capture in auto speed
3. tell me when I should plug in the target — I'll do it after you say go
4. capture for 5 seconds after I plug it in, then stop
5. summarise the bus activity and list all unique device addresses you observed
6. dissect the DATA0/DATA1 packets that followed GET_DESCRIPTOR setups
and pull out the 18-byte device descriptor and the config descriptor
7. switch to facedancer mode, diagnose, then emulate from those descriptors
8. tell me when the clone is up
What works on which devices
Cynthion's analyzer captures USB 1.1 / 2.0 (Low / Full / High speed). SuperSpeed (USB 3.x) lanes are not teed — but virtually every USB 3.x device falls back to USB 2.0 HS when only D+/D- is wired, so most still work.
| Device class | Sniff | Clone via emulate_from_descriptor |
|---|---|---|
| HID — mouse / keyboard / gamepad | ✅ | 🟢 simple (descriptor + report) |
| Serial — FTDI / CH340 / CDC-ACM | ✅ | 🟢 built-in template |
| Mass storage (flash drive) | ✅ — falls back to HS | 🟡 needs SCSI command emulation (facedancer.devices.umass) |
| Webcam (UVC) | ✅ HS | 🔴 isochronous + complex descriptors |
| Audio (UAC) | ✅ | 🔴 isochronous |
| Printer / network adapter (CDC-ECM/RNDIS) | ✅ | 🟡 protocol-dependent |
| Vendor proprietary protocols | ✅ | 🟡 needs a vendor-request handler |
| Pure SuperSpeed-only (rare) | ❌ | ❌ |
⚠️ Flash-drive caveat: a busy HS bulk transfer (~30–60 MB/s) overruns Cynthion's internal FIFO and the gateware emits
CAPTURE_STOP_FULL. For RE work, sniff the enumeration phase only — re-plug the target, then immediately stop the capture once it's connected; don't actually read/write large files while capturing.
Known limitations / non-goals
- Higher-layer dissection (HID, MSC, UVC class) only kicks in once the capture includes the device's enumeration. Re-plug the target while capturing for the richest tshark output.
- SuperSpeed (USB 3.x) is out of scope — the analyzer applet doesn't observe SS lanes.
- The HID keyboard emulator will inject keystrokes into whatever host is connected to TARGET-C. If that's the same machine running the MCP server, those keystrokes land in whichever app has focus. Use sparingly.
Architecture
cynthion-mcp/
├── pyproject.toml
└── src/cynthion_mcp/
├── server.py # FastMCP entrypoint (17 tools)
├── hardware.py # Apollo MCU + bitstream switching, JTAG-stuck recovery
├── capture.py # analyzer.bit USB driver (vendor reqs + bulk drain)
├── decoder.py # Cynthion native frame format → pcap LINKTYPE_USB_2_0
├── tshark.py # tshark JSON wrapper + USB PID name table
└── emulator.py # Facedancer integration (diagnose, template + descriptor emulation)
The capture path drives the analyzer applet directly through libusb (vendor requests 0–4, bulk endpoint 0x81). Capture bytes are streamed to ~/.cynthion-mcp/captures/<id>.bin in the gateware's native frame format (big-endian 16-bit words, 4-byte event records and 4-byte packet headers, 16-bit-aligned). decoder.py re-frames those into a standards-compliant pcap so tshark's USB dissectors (and Wireshark / Packetry) can read them directly.
The emulator path goes through facedancer's Moondancer backend, which talks libgreat-RPC to the SoC firmware running on the FPGA.
Proof of working
See docs/HARDWARE-TEST-LOG.md for a detailed
bring-up log: an LLM driving cynthion-mcp through the full reverse-engineering
loop on a Cynthion r1.4 — sniffing a Logitech wireless receiver, identifying
an unknown Edimax Bluetooth dongle from its descriptors alone (VID/PID/MAC),
and emulating an FTDI device that macOS IOKit registers.
License
BSD 3-Clause — see LICENSE. Same as the upstream Cynthion / LUNA / facedancer projects.
Related projects
- Cynthion — the hardware + host tools this builds on
- LUNA — the Amaranth HDL USB gateware library
- Facedancer — the USB device-emulation framework
- Packetry — GUI viewer for Cynthion-format captures (consumes the same pcaps
convert_to_pcapproduces)
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.