{"id":"5da4301e-af77-49f2-a93b-486bcfc6c099","slug":"mcp-sigdetect","kind":"mcp","name":"mcp-sigdetect","summary":"MCP server: SDR signal analysis (OOK/FSK demod, line-code decoding, protocol ID) callable from Claude or any MCP client","owner":"haxorthematrix","repoUrl":"https://github.com/haxorthematrix/mcp-sigdetect","license":"MIT","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-05-23T10:42:16.000Z","addedAt":"2026-10-21T21:15:02.537Z","logoUrl":"https://avatars.githubusercontent.com/u/273335?u=861f4d95d7d438a0073e5651bc784fd642ad51a8&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools","communication","cloud-infra"],"tags":[],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-27T23:27:23.074Z","sample":false,"url":"https://conduits.dev/tools/mcp-sigdetect","language":"Python","transports":["stdio"],"install":{"pypi":"mcp-sigdetect"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# mcp-sigdetect\n\nAn MCP (Model Context Protocol) server that turns Claude (or any\nMCP-aware client) into an autonomous SDR signal analyst. Hand it an IQ\ncapture and it will work through the methodology in\n[`PLAYBOOK.md`](PLAYBOOK.md) — find bursts, identify the modulation,\ndemodulate, recover bits, decode the line code, extract hex payloads,\nand suggest a protocol — using only `numpy` for DSP. No GNU Radio, no\nexternal decoders required.\n\n## What you get\n\nTwelve callable tools, each a single step from the playbook:\n\n| Tool                  | Purpose                                                  |\n|-----------------------|----------------------------------------------------------|\n| `inspect_iq`          | basic stats + parse filename for sample rate / freq      |\n| `find_bursts`         | activity windowing + cluster retransmission sets         |\n| `identify_modulation` | classify one burst as OOK / FSK / PSK / LoRa / unknown   |\n| `demod_ook`           | hysteresis pulse-train extraction                        |\n| `demod_fsk`           | discriminator + autocorrelation bit-clock recovery       |\n| `decode_pwm`          | pulse-width → bits                                       |\n| `decode_pdm`          | gap-distance → bits, with sync detection                 |\n| `decode_manchester`   | IEEE / G.E. Thomas chip-pair decoding                    |\n| `bits_to_hex`         | pack bits to hex (MSB- or LSB-first)                     |\n| `find_sync`           | search a bit string for a sync byte (any orientation)    |\n| `identify_protocol`   | match (freq, modulation, bitrate, sync) → known protocol |\n| `run_pipeline`        | end-to-end best-effort pass                              |\n\nVerified on three reference captures (433 MHz keyfob, 908 MHz Z-Wave,\n315 MHz beacon). See [Validation](#validation) below.\n\n## Install\n\nRequires Python 3.10+.\n\n```bash\ngit clone https://github.com/haxorthematrix/mcp-sigdetect.git\ncd mcp-sigdetect\npython3 -m venv .venv\nsource .venv/bin/activate          # or .venv\\Scripts\\activate on Windows\npip install -e .\n```\n\nThe install pulls in `mcp[cli]` and `numpy`. That's it.\n\n## Use it from Claude Code\n\n```bash\n# One-time setup\nclaude mcp add sigdetect -- \\\n  /absolute/path/to/mcp-sigdetect/.venv/bin/python -m sigdetect.server\n```\n\nThen in Claude Code:\n\n> *\"Use the sigdetect server to analyze the IQ file at\n> `/path/to/capture.cs16` and tell me what the device is and what it\n> transmitted.\"*\n\nClaude will plan the call sequence (usually `inspect_iq` →\n`find_bursts` → `identify_modulation` → demod → decode → hex →\nprotocol), invoke the tools, and write you a report. You can also\nask it to call specific tools individually if you want manual control.\n\n## Use it from Claude Desktop\n\nEdit `~/Library/Application Support/Claude/claude_desktop_config.json`\n(macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"sigdetect\": {\n      \"command\": \"/absolute/path/to/mcp-sigdetect/.venv/bin/python\",\n      \"args\": [\"-m\", \"sigdetect.server\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop. The tools appear under the connector\nindicator at the bottom of the chat.\n\n## Use it from the Anthropic API / SDK\n\n```python\nimport anthropic\nclient = anthropic.Anthropic()\nmsg = client.messages.create(\n    model=\"claude-opus-4-7\",\n    max_tokens=4096,\n    mcp_servers=[{\n        \"type\": \"stdio\",\n        \"command\": \"/path/to/mcp-sigdetect/.venv/bin/python\",\n        \"args\": [\"-m\", \"sigdetect.server\"],\n    }],\n    messages=[{\n        \"role\": \"user\",\n        \"content\": \"Analyze /path/to/capture.cs16 and report findings\",\n    }],\n)\nprint(msg.content[-1].text)\n```\n\n(The exact MCP attachment surface evolves with the SDK; check the\nAnthropic Python SDK docs at time of use.)\n\n## Use it standalone (no LLM)\n\nYou can drive every tool from a Python REPL:\n\n```python\nfrom sigdetect.server import run_pipeline\nresult = run_pipeline(path=\"my-capture.cs16\",\n                      sample_rate=2_000_000.0,\n                      center_freq_hz=433_920_000.0)\nimport json; print(json.dumps(result, indent=2))\n```\n\nOr call the lower-level modules directly:\n\n```python\nfrom sigdetect.io import load_iq\nfrom sigdetect.activity import find_bursts\nfrom sigdetect.modulation import identify_modulation\nfrom sigdetect.demod import ook_pulses\nfrom sigdetect.encoding import decode_pdm, extract_repeated_blocks, bits_to_hex\n\niq, meta = load_iq(\"my-capture.cs16\")\nbursts = find_bursts(iq, meta.sample_rate)\ns, e = max(bursts, key=lambda b: b[1] - b[0])\nmod = identify_modulation(iq, meta.sample_rate, s, e)\nprint(mod.modulation, mod.confidence)\n\npulses = ook_pulses(iq[s:e], meta.sample_rate)\npdm = decode_pdm(pulses.gap_widths_us)\nreps = extract_repeated_blocks(pdm.bits)\nprint(reps[\"top_block\"], \"x\", reps[\"repeats\"],\n      \"→\", bits_to_hex(reps[\"top_block\"], msb_first=True))\n```\n\n## Use it from URH\n\nThere is a companion fork of URH —\n[`urh-ng-ai`](https://github.com/haxorthematrix/urh-ng-ai) — that adds\nan **AI Deep Analysis** button to each signal panel. The button\nexports the currently-selected IQ slice and routes it through this\nserver. See that repo for installation.\n\n## Supported IQ file formats\n\nFilename extension drives detection; override with `fmt=`:\n\n| Extension                  | dtype        | bytes/sample | typical source       |\n|----------------------------|--------------|--------------|----------------------|\n| `.cs16`, `.complex16s`     | int16 I/Q    | 4            | HackRF default       |\n| `.cu8`                     | uint8 I/Q    | 2            | RTL-SDR native       |\n| `.cs8`                     | int8 I/Q     | 2            |                      |\n| `.cf32`, `.complex`        | float32 I/Q  | 8            | GNU Radio default    |\n\nFilename can encode parameters; common patterns are auto-parsed:\n\n| Pattern                       | Resolves to                          |\n|-------------------------------|--------------------------------------|\n| `433_92MHz` / `915MHz`        | center freq                          |\n| `2MSps` / `8MSps`             | sample rate                          |\n| trailing `-315`               | 315 MHz (compact form)               |\n| trailing `-90842`             | 908.42 MHz (5-digit / 10 kHz units)  |\n| trailing `-4339`              | 433.9 MHz (4-digit / 100 kHz units)  |\n\nIf parsing fails, pass `sample_rate=` and `center_freq_hz=`\nexplicitly to any tool.\n\n## Modulation decision tree\n\n`identify_modulation` runs four tests and reconciles them:\n\n1. **Magnitude variance** (`std/mean` of strong samples) — high\n   variance is consistent with OOK.\n2. **Bimodal magnitude distribution** — high fractions of both\n   near-zero and near-peak samples confirm OOK; near-zero alone\n   indicates low-duty-cycle PDM-style OOK.\n3. **Instantaneous frequency histogram** — two or more clear modes\n   indicate FSK; tone separation gives deviation.\n4. **Spectrum peaks** — recorded for reporting; symmetric ±Δ peaks\n   can be either DSB-SC AM or FSK at ±Δ deviation (test #3\n   discriminates).\n\nPSK and LoRa are recognized but not demodulated end-to-end; hand off\nto GNU Radio / `gr-lora_sdr` if you have one of those.\n\n## Supported encodings\n\n| Encoding        | Recovers from                              | Typical use                |\n|-----------------|--------------------------------------------|----------------------------|\n| **PWM**         | pulse-widths → bit value                   | PT2262, HCS300, EV1527     |\n| **PDM / PIWM**  | gap durations → bit value, sync markers    | gate remotes, weather sensors |\n| **Manchester**  | chip pairs (IEEE or G.E. Thomas)           | TPMS, some RFID            |\n| **NRZ FSK**     | inst-freq sign at bit centers              | Z-Wave R2/R3, BLE          |\n\n`decode_pdm` knows about repeating-block structures (preamble + sync\n+ data block × N) and returns the most-common block via\n`extract_repeated_blocks`. That's how the 433.92 MHz keyfob test case\nrecovers identical 41-bit codes across all repeats.\n\n## Validation\n\nTested against three reference captures (not included in this repo;\nprovide your own). For each, the expected output:\n\n**433.92 MHz gate remote (OOK PDM, two button presses each pressed\ntwice):**\n\n```\nbursts: 4 (in 4 clusters)\nmodulation: OOK (conf 0.97)\nburst 1: hex_msb = 41 90 4D 22 40 00   (14 repeats of 41-bit code)\nburst 2: hex_msb = 41 90 4D 21 3F 00   (13 repeats)\nburst 3: hex_msb = 41 90 4D 22 40 00   (= button 1, repressed)\nburst 4: hex_msb = 41 90 4D 21 3F 00   (= button 2, repressed)\nprotocol: 433.92 MHz remote / weather (OOK PDM or PWM)\n```\n\n**908.42 MHz Z-Wave (GFSK, 100 kbps):**\n\n```\nbursts: 42 (in 4 clusters)\nmodulation: FSK (conf 0.86)  -- ±18 kHz deviation around carrier\nsync byte 0xF0 found in every burst\nprotocol: Z-Wave US (Ch1 908.40 R2, Ch2 908.42 R3)  conf=0.70\n```\n\n**315 MHz OOK beacon (preamble-dominated):**\n\n```\nbursts: 8 (in 4 clusters)\nmodulation: OOK (conf 0.94)\nhex: (no clean data extracted — long alternating preamble, no varied\n      payload visible in the envelope)\nprotocol: 315 MHz car RKE / TPMS / garage opener (OOK)\n```\n\nA `test_pipeline.py` is included that runs `run_pipeline()` against any\nfile path you point it at.\n\n## Known limitations\n\n- **FSK bit-rate estimation** on whitened / random data is hard\n  because there's no periodic pattern for the autocorrelator to lock\n  to. `run_pipeline` applies protocol-aware hints (e.g. Z-Wave US →\n  retry at 100 kbps). For other protocols, pass `bit_rate_bps`\n  explicitly to `demod_fsk`.\n- **PSK and LoRa** are not demodulated. Identification only.\n- **Big captures** (>100 M samples) work but the hysteresis OOK loop\n  is a Python `for` and gets slow. Rewrite in Cython if you need it\n  hot.\n- **DPSK, DBPSK, OQPSK, MSK, GMSK** beyond what GFSK demod recovers\n  via discriminator — handle with GNU Radio.\n\n## Contributing\n\nPRs welcome, especially:\n\n- More entries in `sigdetect/protocol.py` (any 868 / 915 MHz protocol)\n- A real PSK / Costas-loop demodulator\n- LoRa de-chirp + Hamming + whitening\n- Better bit-rate estimation for whitened FSK (PLL or Gardner loop)\n\nRun `validate.py` against your test data before sending the PR.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n\n## See also\n\n- [`PLAYBOOK.md`](PLAYBOOK.md) — the methodology this server implements\n- [`urh-ng-ai`](https://github.com/haxorthematrix/urh-ng-ai) — companion\n  URH fork with an \"AI Deep Analysis\" button that calls this server\n- [Anthropic MCP](https://modelcontextprotocol.io) — protocol docs\n"}