|
| 1 | +# MIOS MCP Server |
| 2 | + |
| 3 | +MCP (Model Context Protocol) server that allows AI assistants to interact |
| 4 | +with MIOS devices over USB. Supports CLI command execution, memory reads, |
| 5 | +firmware flashing (DFU and ST-Link), and signal capture. |
| 6 | + |
| 7 | +## Device-side setup |
| 8 | + |
| 9 | +Add the USB interfaces to your platform's interface queue: |
| 10 | + |
| 11 | +```c |
| 12 | +#include <usb/usb.h> |
| 13 | + |
| 14 | +struct usb_interface_queue q; |
| 15 | +STAILQ_INIT(&q); |
| 16 | + |
| 17 | +usb_cdc_create_shell(&q); // Optional: serial console |
| 18 | +usb_dfu_runtime_create(&q); // Optional: in-app DFU detach |
| 19 | +usb_mcp_create(&q, 0x01); // MCP command interface (subclass 0x01) |
| 20 | + |
| 21 | +your_platform_usb_create(vid, pid, manufacturer, product, &q); |
| 22 | +``` |
| 23 | +
|
| 24 | +The MCP command interface uses USB vendor class (0xFF) with subclass |
| 25 | +0x01. Sigcapture uses subclass 192. These are separate USB interfaces. |
| 26 | +
|
| 27 | +Rebuild and flash your firmware. |
| 28 | +
|
| 29 | +## Building the MCP server |
| 30 | +
|
| 31 | +``` |
| 32 | +make -C host/mcp |
| 33 | +``` |
| 34 | +
|
| 35 | +Produces `host/mcp/mios-mcp`. Requires `libusb-1.0` development |
| 36 | +headers (`apt install libusb-1.0-0-dev` on Debian/Ubuntu). |
| 37 | +
|
| 38 | +## Claude Code integration |
| 39 | +
|
| 40 | +Add to `~/.claude.json`: |
| 41 | +
|
| 42 | +```json |
| 43 | +{ |
| 44 | + "mcpServers": { |
| 45 | + "mios": { |
| 46 | + "command": "/path/to/mios/host/mcp/mios-mcp" |
| 47 | + } |
| 48 | + } |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +Restart Claude Code. The following tools become available: |
| 53 | + |
| 54 | +### configure |
| 55 | + |
| 56 | +Set the USB VID/PID used to find the MIOS device. Affects all |
| 57 | +subsequent tool calls. Default VID is 0x6666, PID is 0 (match any). |
| 58 | + |
| 59 | +``` |
| 60 | +configure(vid: 0x1234, pid: 0x5678) |
| 61 | +``` |
| 62 | + |
| 63 | +### cli |
| 64 | + |
| 65 | +Send a CLI command to the device via the USB MCP interface and get |
| 66 | +the text output back. |
| 67 | + |
| 68 | +``` |
| 69 | +cli(command: "uptime") |
| 70 | +cli(command: "i2c scan 0", timeout_ms: 10000) |
| 71 | +``` |
| 72 | + |
| 73 | +### read_memory |
| 74 | + |
| 75 | +Read raw memory from the device (max 32 bytes per call). Returns |
| 76 | +a hex dump. |
| 77 | + |
| 78 | +``` |
| 79 | +read_memory(address: 0x08000000, length: 32) |
| 80 | +``` |
| 81 | + |
| 82 | +### flash_dfu |
| 83 | + |
| 84 | +Flash firmware via USB DFU. Automatically handles DFU Runtime detach |
| 85 | +if the device is running (requires `usb_dfu_runtime_create` on device). |
| 86 | + |
| 87 | +``` |
| 88 | +flash_dfu(elf_path: "/path/to/build.elf") |
| 89 | +flash_dfu(elf_path: "/path/to/build.elf", force: true) |
| 90 | +``` |
| 91 | + |
| 92 | +### flash_stlink |
| 93 | + |
| 94 | +Flash firmware via a running OpenOCD instance (TCL interface, default |
| 95 | +port 6666). Auto-detects flash vs RAM targets from the ELF load address. |
| 96 | + |
| 97 | +``` |
| 98 | +flash_stlink(elf_path: "/path/to/build.elf") |
| 99 | +flash_stlink(elf_path: "/path/to/build.elf", host: "127.0.0.1", port: 6666) |
| 100 | +``` |
| 101 | + |
| 102 | +### sigcapture_list |
| 103 | + |
| 104 | +List recent signal captures stored in memory. A background thread |
| 105 | +continuously receives captures from the device and stores up to 16 |
| 106 | +in a ring buffer. Returns metadata only (no sample data). |
| 107 | + |
| 108 | +``` |
| 109 | +sigcapture_list() |
| 110 | +``` |
| 111 | + |
| 112 | +### sigcapture_save |
| 113 | + |
| 114 | +Export a capture to CSV for analysis. The CSV has a `time_s` column |
| 115 | +(relative to trigger) followed by one column per channel with scaled |
| 116 | +values. Use the capture ID from `sigcapture_list`. |
| 117 | + |
| 118 | +``` |
| 119 | +sigcapture_save(id: 42, path: "/tmp/capture.csv") |
| 120 | +``` |
| 121 | + |
| 122 | +The CSV can be loaded with Python/pandas/matplotlib for analysis. |
| 123 | + |
| 124 | +## USB MCP command protocol |
| 125 | + |
| 126 | +The MCP command interface (`usb_mcp_create`, subclass 0x01) uses |
| 127 | +64-byte bulk transfers with a single-byte message type prefix: |
| 128 | + |
| 129 | +| Type | Direction | Name | Payload | |
| 130 | +|------|------------- |------------------|----------------------------------| |
| 131 | +| 0x01 | Host->Device | CLI Execute | Command string (max 63 bytes) | |
| 132 | +| 0x02 | Device->Host | CLI Response | Output text (up to 63 bytes) | |
| 133 | +| 0x03 | Device->Host | CLI Complete | int32_t error code (LE) | |
| 134 | +| 0x04 | Host->Device | Read Memory | uint32_t addr + uint32_t length | |
| 135 | +| 0x05 | Device->Host | Memory Response | Raw data bytes | |
| 136 | + |
| 137 | +CLI flow: host sends 0x01, device sends N x 0x02 packets with output |
| 138 | +text, then 0x03 with the error code. |
| 139 | + |
| 140 | +## Sigcapture protocol |
| 141 | + |
| 142 | +Sigcapture uses a separate vendor-class USB interface (subclass 192) |
| 143 | +with a single bulk IN endpoint. The device pushes capture data after |
| 144 | +a trigger event. The MCP server receives this in a background thread. |
| 145 | + |
| 146 | +Packet dispatch by size: |
| 147 | +- 10 bytes: preamble (channels, depth, sample rate, trigger offset) |
| 148 | +- 20 bytes: channel descriptor (name, unit, scale) |
| 149 | +- 64 bytes: data (columnar int16_t samples) |
| 150 | +- 1 byte: trailer (0xfe, capture complete) |
0 commit comments