RadioChron is a pure-Rust Wi-Fi diagnostics library and MCP server. It records what the radio actually did — associations, roams, drops, reason codes — and returns verdicts, not data dumps. A snapshot says “−58 dBm, fine”. The chronicle says why it wasn't fine ten minutes ago.
# library — crates.io publish pending; straight from git today $ cargo add radiochron --git https://github.com/sergii-ziborov/radiochron # MCP server — build once, point your client at the binary $ git clone https://github.com/sergii-ziborov/radiochron && cd radiochron $ cargo build --release # → target/release/radiochron.exe $ claude mcp add radiochron -- ./target/release/radiochron.exe
RadioChron reads the Windows WLAN event log as structured, locale-independent data — numeric event IDs and reason codes, never localized message text — and separates roams from faults by ConnectionId continuity: a roam reuses the session id, a genuine reconnect allocates a new one. Security rekeys, resume-from-sleep and same-network roaming are suppressed instead of counted as failures.
{
"id": "reconnect_loop",
"severity": "critical",
"title": "5 connection attempts and 5 failures in 120s",
"detail": { "attempts": 5, "failures": 5, "successes": 0,
"reason_codes": [229396, 229396, 229378] },
"caveat": "Roams and rekeys are excluded by requiring distinct
ConnectionIds and real failures. Reason codes are
undocumented observations; treat unfamiliar ones as
unknown rather than meaningful."
}
caveat — the honest reason it might be wrong.
A bare severity invites over-trust; error bars are part of the payload.A stable link polled every 2 seconds is 43,200 identical log lines a day. The chronicle
writes one associated line and goes quiet until something
changes: a roam, a drop, a signal shift beyond dB-hysteresis, a reason code in the event
log. Storage is append-only JSONL with built-in rotation — greppable, schema-free, and a
hard disk ceiling of ~4 MiB by default, sized for embedded flash, not workstations.
// pluggable Sink trait — the shipped sink is JSONL + rotation. // a SQLite sink is ~30 lines in *your* crate; bundling one would // drag in a C compile and break the bare-rustup build. let sink = JsonlSink::open("chronicle.jsonl", RotationPolicy::default())?; let mut rec = Recorder::new(sink, RecorderOptions::default()); rec.run_for(Duration::from_secs(3600))?; // or own the loop: rec.step()
Field devices lose Wi-Fi constantly and can't say why. The Rust options that exist either
shell out to netsh/iw and parse localized text, or resolve to a
51-crate tree that doesn't build on a bare rustup toolchain at all — its transitive
dependencies want mingw or the Visual C++ build tools. On an embedded image that's the
difference between adding a dependency and rebuilding the OS.
RadioChron hand-writes its FFI and resolves system libraries at runtime. No binding crates, no import libraries, no C. A device that only needs association state compiles just that: features are granular by capability.
# a sensor that only reports link state pulls exactly that: radiochron = { git = "…", default-features = false, features = ["status"] } # status · scan · analyze · sample · history · record
radiochron — collectors, 802.11 analysis, the chronicle.
What an IoT agent, exporter or CLI embeds. MIT, three dependencies.radiochron-mcp — six read-only tools + resources over stdio.
Point Claude, Codex or any MCP client at one binary. No config, no arguments.radiochron on npm via napi-rs with prebuilt binaries —
import from Node.js without a Rust toolchain. Name reserved-checked, build pending.| tool | answers |
|---|---|
wifi_history | “why did it drop earlier” — reconnect loops, an AP failing key exchange, credential mismatch // the headline |
wifi_analyze | findings, not records: contention, weak signal, band-steering candidates — 802 bytes where the raw list costs 41 KB |
wifi_sample | “is it stable” — RSSI swing, roam count, drops over a bounded window |
wifi_status | the association right now: SSID, BSSID, PHY, rates, RSSI |
wifi_networks | nearby BSS with band, channel, dBm and security flags; summary by default |
wifi_scan | ask the driver for a fresh scan |
Deliberately absent: saved Wi-Fi keys, MAC changes, adapter
restarts, LAN sweeps. An autonomous model must not be able to leak a credential or drop you
off the network. Calling them returns -32601.
netsh wlan show wlanreport
already groups three days of history into sessions. If a human reading a screen is your
workflow, use them — they're good.netsh gives the
least of any platform — and where we started. Linux gives more data via nl80211
and is next.