Skip to content
Conduit

mcp server

mcp-sigdetect

MCP server: SDR signal analysis (OOK/FSK demod, line-code decoding, protocol ID) callable from Claude or any MCP client

haxorthematrix/mcp-sigdetectcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "mcp-sigdetect": {
      "command": "uvx",
      "args": [
        "mcp-sigdetect"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 27, 2026

mcp-sigdetect

An MCP (Model Context Protocol) server that turns Claude (or any MCP-aware client) into an autonomous SDR signal analyst. Hand it an IQ capture and it will work through the methodology in PLAYBOOK.md — find bursts, identify the modulation, demodulate, recover bits, decode the line code, extract hex payloads, and suggest a protocol — using only numpy for DSP. No GNU Radio, no external decoders required.

What you get

Twelve callable tools, each a single step from the playbook:

ToolPurpose
inspect_iqbasic stats + parse filename for sample rate / freq
find_burstsactivity windowing + cluster retransmission sets
identify_modulationclassify one burst as OOK / FSK / PSK / LoRa / unknown
demod_ookhysteresis pulse-train extraction
demod_fskdiscriminator + autocorrelation bit-clock recovery
decode_pwmpulse-width → bits
decode_pdmgap-distance → bits, with sync detection
decode_manchesterIEEE / G.E. Thomas chip-pair decoding
bits_to_hexpack bits to hex (MSB- or LSB-first)
find_syncsearch a bit string for a sync byte (any orientation)
identify_protocolmatch (freq, modulation, bitrate, sync) → known protocol
run_pipelineend-to-end best-effort pass

Verified on three reference captures (433 MHz keyfob, 908 MHz Z-Wave, 315 MHz beacon). See Validation below.

Install

Requires Python 3.10+.

git clone https://github.com/haxorthematrix/mcp-sigdetect.git
cd mcp-sigdetect
python3 -m venv .venv
source .venv/bin/activate          # or .venv\Scripts\activate on Windows
pip install -e .

The install pulls in mcp[cli] and numpy. That's it.

Use it from Claude Code

# One-time setup
claude mcp add sigdetect -- \
  /absolute/path/to/mcp-sigdetect/.venv/bin/python -m sigdetect.server

Then in Claude Code:

"Use the sigdetect server to analyze the IQ file at /path/to/capture.cs16 and tell me what the device is and what it transmitted."

Claude will plan the call sequence (usually inspect_iq → find_bursts → identify_modulation → demod → decode → hex → protocol), invoke the tools, and write you a report. You can also ask it to call specific tools individually if you want manual control.

Use it from Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "sigdetect": {
      "command": "/absolute/path/to/mcp-sigdetect/.venv/bin/python",
      "args": ["-m", "sigdetect.server"]
    }
  }
}

Restart Claude Desktop. The tools appear under the connector indicator at the bottom of the chat.

Use it from the Anthropic API / SDK

import anthropic
client = anthropic.Anthropic()
msg = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=4096,
    mcp_servers=[{
        "type": "stdio",
        "command": "/path/to/mcp-sigdetect/.venv/bin/python",
        "args": ["-m", "sigdetect.server"],
    }],
    messages=[{
        "role": "user",
        "content": "Analyze /path/to/capture.cs16 and report findings",
    }],
)
print(msg.content[-1].text)

(The exact MCP attachment surface evolves with the SDK; check the Anthropic Python SDK docs at time of use.)

Use it standalone (no LLM)

You can drive every tool from a Python REPL:

from sigdetect.server import run_pipeline
result = run_pipeline(path="my-capture.cs16",
                      sample_rate=2_000_000.0,
                      center_freq_hz=433_920_000.0)
import json; print(json.dumps(result, indent=2))

Or call the lower-level modules directly:

from sigdetect.io import load_iq
from sigdetect.activity import find_bursts
from sigdetect.modulation import identify_modulation
from sigdetect.demod import ook_pulses
from sigdetect.encoding import decode_pdm, extract_repeated_blocks, bits_to_hex

iq, meta = load_iq("my-capture.cs16")
bursts = find_bursts(iq, meta.sample_rate)
s, e = max(bursts, key=lambda b: b[1] - b[0])
mod = identify_modulation(iq, meta.sample_rate, s, e)
print(mod.modulation, mod.confidence)

pulses = ook_pulses(iq[s:e], meta.sample_rate)
pdm = decode_pdm(pulses.gap_widths_us)
reps = extract_repeated_blocks(pdm.bits)
print(reps["top_block"], "x", reps["repeats"],
      "→", bits_to_hex(reps["top_block"], msb_first=True))

Use it from URH

There is a companion fork of URH — urh-ng-ai — that adds an AI Deep Analysis button to each signal panel. The button exports the currently-selected IQ slice and routes it through this server. See that repo for installation.

Supported IQ file formats

Filename extension drives detection; override with fmt=:

Extensiondtypebytes/sampletypical source
.cs16, .complex16sint16 I/Q4HackRF default
.cu8uint8 I/Q2RTL-SDR native
.cs8int8 I/Q2
.cf32, .complexfloat32 I/Q8GNU Radio default

Filename can encode parameters; common patterns are auto-parsed:

PatternResolves to
433_92MHz / 915MHzcenter freq
2MSps / 8MSpssample rate
trailing -315315 MHz (compact form)
trailing -90842908.42 MHz (5-digit / 10 kHz units)
trailing -4339433.9 MHz (4-digit / 100 kHz units)

If parsing fails, pass sample_rate= and center_freq_hz= explicitly to any tool.

Modulation decision tree

identify_modulation runs four tests and reconciles them:

  1. Magnitude variance (std/mean of strong samples) — high variance is consistent with OOK.
  2. Bimodal magnitude distribution — high fractions of both near-zero and near-peak samples confirm OOK; near-zero alone indicates low-duty-cycle PDM-style OOK.
  3. Instantaneous frequency histogram — two or more clear modes indicate FSK; tone separation gives deviation.
  4. Spectrum peaks — recorded for reporting; symmetric ±Δ peaks can be either DSB-SC AM or FSK at ±Δ deviation (test #3 discriminates).

PSK and LoRa are recognized but not demodulated end-to-end; hand off to GNU Radio / gr-lora_sdr if you have one of those.

Supported encodings

EncodingRecovers fromTypical use
PWMpulse-widths → bit valuePT2262, HCS300, EV1527
PDM / PIWMgap durations → bit value, sync markersgate remotes, weather sensors
Manchesterchip pairs (IEEE or G.E. Thomas)TPMS, some RFID
NRZ FSKinst-freq sign at bit centersZ-Wave R2/R3, BLE

decode_pdm knows about repeating-block structures (preamble + sync

  • data block × N) and returns the most-common block via extract_repeated_blocks. That's how the 433.92 MHz keyfob test case recovers identical 41-bit codes across all repeats.

Validation

Tested against three reference captures (not included in this repo; provide your own). For each, the expected output:

433.92 MHz gate remote (OOK PDM, two button presses each pressed twice):

bursts: 4 (in 4 clusters)
modulation: OOK (conf 0.97)
burst 1: hex_msb = 41 90 4D 22 40 00   (14 repeats of 41-bit code)
burst 2: hex_msb = 41 90 4D 21 3F 00   (13 repeats)
burst 3: hex_msb = 41 90 4D 22 40 00   (= button 1, repressed)
burst 4: hex_msb = 41 90 4D 21 3F 00   (= button 2, repressed)
protocol: 433.92 MHz remote / weather (OOK PDM or PWM)

908.42 MHz Z-Wave (GFSK, 100 kbps):

bursts: 42 (in 4 clusters)
modulation: FSK (conf 0.86)  -- ±18 kHz deviation around carrier
sync byte 0xF0 found in every burst
protocol: Z-Wave US (Ch1 908.40 R2, Ch2 908.42 R3)  conf=0.70

315 MHz OOK beacon (preamble-dominated):

bursts: 8 (in 4 clusters)
modulation: OOK (conf 0.94)
hex: (no clean data extracted — long alternating preamble, no varied
      payload visible in the envelope)
protocol: 315 MHz car RKE / TPMS / garage opener (OOK)

A test_pipeline.py is included that runs run_pipeline() against any file path you point it at.

Known limitations

  • FSK bit-rate estimation on whitened / random data is hard because there's no periodic pattern for the autocorrelator to lock to. run_pipeline applies protocol-aware hints (e.g. Z-Wave US → retry at 100 kbps). For other protocols, pass bit_rate_bps explicitly to demod_fsk.
  • PSK and LoRa are not demodulated. Identification only.
  • Big captures (>100 M samples) work but the hysteresis OOK loop is a Python for and gets slow. Rewrite in Cython if you need it hot.
  • DPSK, DBPSK, OQPSK, MSK, GMSK beyond what GFSK demod recovers via discriminator — handle with GNU Radio.

Contributing

PRs welcome, especially:

  • More entries in sigdetect/protocol.py (any 868 / 915 MHz protocol)
  • A real PSK / Costas-loop demodulator
  • LoRa de-chirp + Hamming + whitening
  • Better bit-rate estimation for whitened FSK (PLL or Gardner loop)

Run validate.py against your test data before sending the PR.

License

MIT. See LICENSE.

See also

  • PLAYBOOK.md — the methodology this server implements
  • urh-ng-ai — companion URH fork with an "AI Deep Analysis" button that calls this server
  • Anthropic MCP — protocol docs
↑↓ navigate openesc close