get_wifi_info
get_wifi_info
Reports current Wi-Fi connection details: SSID, signal strength (RSSI dBm), channel, band, security type, and link speed. Use to diagnose Wi-Fi performance or intermittent connectivity.
Metadata
Name
get_wifi_info
Updated
3 weeks ago
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/getWifiInfo.ts — get_wifi_info skill
*
* Reports current Wi-Fi connection details: SSID, signal strength (RSSI dBm),
* channel, band, security type, and link speed. Use to diagnose Wi-Fi
* performance or intermittent connectivity.
*
* Platform strategy
* -----------------
* darwin Probe sequence (no single CLI works post-Sequoia):
* 1. `networksetup -listallhardwareports` — discover the Wi-Fi
* device (typically en0 but not guaranteed)
* 2. `ifconfig <device>` — confirm the interface is UP+RUNNING with
* an inet address (the authoritative isConnected signal)
* 3. CoreWLAN via JXA (`osascript -l JavaScript`) — PRIMARY signal
* source. RSSI / noise / channel / band / txRate / security are
* NOT gated behind Location Services, so this returns real signal
* data even when the calling app lacks the CoreLocation grant.
* 4. `system_profiler SPAirPortDataType` — FALLBACK only (used when
* the CoreWLAN probe fails to produce an RSSI).
* The legacy `airport -I` was deprecated in macOS 14.4 (returns only a
* deprecation warning) — DO NOT USE. `system_profiler`'s "Current
* Network Information" block (SSID *and* signal) is itself gated behind
* CoreLocation on macOS 14+, so relying on it alone returned all-null
* signal on machines where the agent lacks the location grant — which
* is exactly why CoreWLAN is now primary. SSID / BSSID remain
* location-gated on EVERY API; when withheld, `ssid` is null and
* `ssidAvailable` is false while RSSI / linkQuality are still accurate.
* win32 PowerShell `netsh wlan show interfaces` — parses text output
*
* Smoke test
* npx tsx -r dotenv/config mcp/skills/getWifiInfo.ts
*/
import * as os from "os";
import { exec, execFile } from "child_process";
import { promisify } from "util";
import { z } from "zod";
const execAsync = promisify(exec);
const execFileAsync = promisify(execFile);
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "get_wifi_info",
description:
"Reports current Wi-Fi connection details: SSID, signal strength (RSSI dBm), " +
"channel, band, security type, and link speed. " +
"Use to diagnose Wi-Fi performance or intermittent connectivity.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: false,
affectedScope: ["user"],
auditRequired: false,
schema: {},
} as const;
// -- Types --------------------------------------------------------------------
type LinkQuality = "excellent" | "good" | "fair" | "poor" | "unknown";
interface WifiInfoResult {
device: string | null;
ssid: string | null;
ssidAvailable: boolean;
bssid: string | null;
rssi: number | null;
noise: number | null;
snr: number | null;
channel: number | null;
band: string | null;
security: string | null;
txRateMbps: number | null;
linkQuality: LinkQuality;
isConnected: boolean;
platform: string;
}
// -- PowerShell helper --------------------------------------------------------
async function runPS(script: string): Promise<string> {
const encoded = Buffer.from(script, "utf16le").toString("base64");
const { stdout } = await execAsync(
`powershell.exe -NoProfile -NonInteractive -EncodedCommand ${encoded}`,
{ maxBuffer: 10 * 1024 * 1024 },
);
return stdout.trim();
}
// -- Helpers ------------------------------------------------------------------
function computeLinkQuality(rssi: number | null): LinkQuality {
if (rssi === null) return "unknown";
if (rssi > -50) return "excellent";
if (rssi > -60) return "good";
if (rssi > -70) return "fair";
return "poor";
}
function parseKeyValue(output: string): Map<string, string> {
const map = new Map<string, string>();
for (const line of output.split("\n")) {
const colonIdx = line.indexOf(":");
if (colonIdx === -1) continue;
const key = line.slice(0, colonIdx).trim();
const value = line.slice(colonIdx + 1).trim();
if (key) map.set(key, value);
}
return map;
}
// -- darwin implementation ----------------------------------------------------
async function findWifiDeviceDarwin(): Promise<string | null> {
try {
const { stdout } = await execAsync("networksetup -listallhardwareports");
const blocks = stdout.split(/\n\s*\n/);
for (const block of blocks) {
const portMatch = block.match(/Hardware Port:\s*(.+)/);
const deviceMatch = block.match(/Device:\s*(\S+)/);
if (portMatch && deviceMatch && portMatch[1].trim() === "Wi-Fi") {
return deviceMatch[1].trim();
}
}
return null;
} catch {
return null;
}
}
async function probeWifiLinkDarwin(device: string): Promise<{
up: boolean;
hasIp: boolean;
ipv4: string | null;
}> {
try {
const { stdout } = await execAsync(`ifconfig '${device}'`);
const flags = stdout.match(/flags=\S+\s*<([^>]*)>/)?.[1] ?? "";
const flagSet = new Set(flags.split(","));
const up = flagSet.has("UP") && flagSet.has("RUNNING");
const ipv4Match = stdout.match(/inet (\d+\.\d+\.\d+\.\d+)/);
return { up, hasIp: !!ipv4Match, ipv4: ipv4Match?.[1] ?? null };
} catch {
return { up: false, hasIp: false, ipv4: null };
}
}
async function getWifiInfoFromCoreWLAN(): Promise<CoreWlanWifiInfo | null> {
const jxa = `
ObjC.import('CoreWLAN');
var i = $.CWWiFiClient.sharedWiFiClient.interface;
function n(v){ v = (v && v.js !== undefined) ? v.js : v; var x = Number(v); return isNaN(x) ? null : x; }
function s(v){ return v ? ObjC.unwrap(v) : null; }
var ch = i.wlanChannel;
JSON.stringify({
ssid: s(i.ssid), bssid: s(i.bssid),
rssi: n(i.rssiValue), noise: n(i.noiseMeasurement), txRate: n(i.transmitRate),
channel: ch ? n(ch.channelNumber) : null,
band: ch ? n(ch.channelBand) : null,
security: n(i.security)
});
`.replace(/\n\s*/g, " ").trim();
try {
const { stdout } = await execFileAsync(
"osascript", ["-l", "JavaScript", "-e", jxa], { timeout: 5_000 },
);
const r = JSON.parse(stdout.trim()) as {
ssid: string | null; bssid: string | null;
rssi: number | null; noise: number | null; txRate: number | null;
channel: number | null; band: number | null; security: number | null;
};
return {
ssid: r.ssid || null,
bssid: r.bssid || null,
rssi: r.rssi && r.rssi !== 0 ? r.rssi : null,
noise: r.noise && r.noise !== 0 ? r.noise : null,
txRateMbps: r.txRate && r.txRate > 0 ? r.txRate : null,
channel: r.channel && r.channel > 0 ? r.channel : null,
band: r.band != null ? (CW_BAND[r.band] ?? null) : null,
security: r.security != null ? (CW_SECURITY[r.security] ?? null) : null,
};
} catch {
return null;
}
}
async function getWifiInfoFromSystemProfiler(): Promise<SystemProfilerWifiInfo | null> {
let output = "";
try {
const { stdout } = await execAsync(
"system_profiler SPAirPortDataType",
{ maxBuffer: 5 * 1024 * 1024, timeout: 10_000 },
);
output = stdout;
} catch {
return null;
}
const idx = output.indexOf("Current Network Information:");
if (idx === -1) return null;
const lines = output.slice(idx).split("\n");
let ssidLine: string | null = null;
let ssidLineIdx = -1;
for (let i = 1; i < lines.length && i < 8; i++) {
const trimmed = lines[i].trim();
if (trimmed.length === 0) continue;
if (trimmed.endsWith(":")) {
ssidLine = trimmed.replace(/:\s*$/, "");
ssidLineIdx = i;
break;
}
}
if (!ssidLine || ssidLineIdx === -1) return null;
const isRedacted = ssidLine === "<redacted>";
const ssid = isRedacted ? null : ssidLine;
const ssidAvailable = !isRedacted;
const kv = new Map<string, string>();
for (let i = ssidLineIdx + 1; i < lines.length; i++) {
const raw = lines[i];
if (raw.trim().length === 0) break;
if (!/^\s/.test(raw)) break;
const colonIdx = raw.indexOf(":");
if (colonIdx === -1) continue;
const key = raw.slice(0, colonIdx).trim();
const value = raw.slice(colonIdx + 1).trim();
if (key && value) kv.set(key, value);
}
let channel: number | null = null;
let band: string | null = null;
const channelRaw = kv.get("Channel");
if (channelRaw) {
const m = channelRaw.match(/^\d+(?:\s*\(([^)]+)\))?/);
if (m) {
channel = parseInt(m[1], 10);
const bandHint = m[2] ?? "";
if (bandHint.includes("6GHz")) band = "6 GHz";
else if (bandHint.includes("5GHz")) band = "5 GHz";
else if (bandHint.includes("2GHz") || bandHint.includes("2.4GHz")) band = "2.4 GHz";
else band = channel <= 14 ? "2.4 GHz" : "5 GHz";
}
}
let rssi: number | null = null;
const signalRaw = kv.get("Signal / Noise");
if (signalRaw) {
const m = signalRaw.match(/(-?\d+)\s*dBm/);
if (m) rssi = parseInt(m[1], 10);
}
let txRateMbps: number | null = null;
const txRaw = kv.get("Transmit Rate") ?? kv.get("Last Tx Rate");
if (txRaw) {
const num = parseFloat(txRaw);
if (!isNaN(num) && num > 0) txRateMbps = num;
}
return {
ssid,
ssidAvailable,
channel,
band,
rssi,
txRateMbps,
security: kv.get("Security") ?? null,
};
}
async function getWifiInfoDarwin(): Promise<WifiInfoResult> {
const empty = (device: string | null): WifiInfoResult => ({
device,
ssid: null,
ssidAvailable: false,
bssid: null,
rssi: null,
noise: null,
snr: null,
channel: null,
band: null,
security: null,
txRateMbps: null,
linkQuality: "unknown",
isConnected: false,
platform: "darwin",
});
const device = await findWifiDeviceDarwin();
if (!device) return empty(null);
const link = await probeWifiLinkDarwin(device);
if (!link.up || !link.hasIp) return empty(device);
const cw = await getWifiInfoFromCoreWLAN();
const sp = (!cw || cw.rssi === null) ? await getWifiInfoFromSystemProfiler() : null;
const rssi = cw?.rssi ?? sp?.rssi ?? null;
const noise = cw?.noise ?? null;
const ssid = cw?.ssid ?? sp?.ssid ?? null;
const channel = cw?.channel ?? sp?.channel ?? null;
return {
device,
ssid,
ssidAvailable: Boolean(ssid),
bssid: cw?.bssid ?? null,
rssi,
noise,
snr: rssi !== null && noise !== null ? rssi - noise : null,
channel,
band: cw?.band ?? sp?.band
?? (channel !== null ? (channel <= 14 ? "2.4 GHz" : "5 GHz") : null),
security: cw?.security ?? sp?.security ?? null,
txRateMbps: cw?.txRateMbps ?? sp?.txRateMbps ?? null,
linkQuality: computeLinkQuality(rssi),
isConnected: true,
platform: "darwin",
};
}
async function getWifiInfoWin32(): Promise<WifiInfoResult> {
let output = "";
try {
const ps = `
$ErrorActionPreference = 'SilentlyContinue'
netsh wlan show interfaces`.trim();
output = await runPS(ps);
} catch {
return {
device: null, ssid: null, ssidAvailable: false, bssid: null,
rssi: null, noise: null, snr: null,
channel: null, band: null, security: null, txRateMbps: null,
linkQuality: "unknown", isConnected: false, platform: "win32",
};
}
const kv = parseKeyValue(output);
const device = kv.get("Name") ?? null;
const ssid = kv.get("SSID") ?? kv.get(" SSID") ?? null;
const bssid = kv.get("BSSID") ?? null;
const sigStr = kv.get("Signal");
const chanStr = kv.get("Channel");
const radioType = kv.get("Radio type") ?? null;
const auth = kv.get("Authentication") ?? null;
const rxStr = kv.get("Receive rate (Mbps)");
const txStr = kv.get("Transmit rate (Mbps)");
let rssi: number | null = null;
if (sigStr) {
const sigPct = parseInt(sigStr.replace("%", ""), 10);
if (!isNaN(sigPct)) {
rssi = Math.round((sigPct / 2) - 100);
}
}
const channel = chanStr ? parseInt(chanStr, 10) : null;
const band = radioType?.includes("802.11a") || radioType?.includes("802.11n") || radioType?.includes("802.11ac")
? (channel && channel > 14 ? "5 GHz" : "2.4 GHz")
: null;
const txRateMbps = txStr ? parseFloat(txStr) : (rxStr ? parseFloat(rxStr) : null);
const isConnected = ssid !== null && ssid !== "";
return {
device,
ssid,
ssidAvailable: ssid !== null,
bssid,
rssi,
noise: null,
snr: null,
channel: isNaN(channel ?? NaN) ? null : channel,
band,
security: auth,
txRateMbps: isNaN(txRateMbps ?? NaN) ? null : txRateMbps,
linkQuality: computeLinkQuality(rssi),
isConnected,
platform: "win32",
};
}
// -- Exported run function ----------------------------------------------------
export async function run(_args: Record<string, never> = {}) {
const platform = os.platform();
return platform === "win32"
? getWifiInfoWin32()
: getWifiInfoDarwin();
}
// -- Smoke test ---------------------------------------------------------------
if (false) {
run({})
.then(r => console.log(JSON.stringify(r, null, 2)))
.catch((err: Error) => { console.error(err.message); process.exit(1); });
}