wpa-mcp

dogkeeper886/wpa-mcp

3.2

If you are the rightful owner of wpa-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

MCP (Model Context Protocol) Server for WiFi control via wpa_supplicant.

Tools
5
Resources
0
Prompts
0

wpa-mcp

MCP (Model Context Protocol) Server for WiFi control via wpa_supplicant.

This server runs on a Linux host and allows Claude/MCP clients to:

  • Connect/disconnect WiFi networks (WPA-PSK and WPA2-Enterprise/802.1X)
  • Scan for available networks
  • Debug connection issues with filtered wpa_supplicant logs
  • Check network connectivity and captive portals
  • Run Playwright browser automation scripts

Quick Start (Local)

# 1. Install dependencies and build
npm install
npm run build

# 2. Set up wpa_supplicant (see "wpa_supplicant Setup" below)

# 3. Start the server (replace wlan0 with your interface)
WIFI_INTERFACE=wlan0 npm start

# 4. Register with Claude Code (in another terminal)
claude mcp add wpa-mcp --transport http http://localhost:3000/mcp

Configuration

Copy .env.example to .env and configure:

PORT=3000
HOST=0.0.0.0
WIFI_INTERFACE=wlan0

Makefile Commands

CommandDescription
make startStart server in background
make stopStop server
make restartRestart server
make logsTail log file
make statusCheck if server is running
make cleanRemove dist/

For build/install, use npm directly: npm install, npm run build, npm run start.

wpa_supplicant Setup

Before the server can control WiFi, wpa_supplicant must be configured properly.

1. Find your WiFi interface

ip link show | grep -E "^[0-9]+: wl"
# Example output: 4: wlan0: <BROADCAST,MULTICAST> ...

2. Disable NetworkManager for WiFi interface (if running)

# Check if NetworkManager is managing your interface
nmcli device status

# If managed, tell NetworkManager to ignore it
sudo nmcli device set wlan0 managed no

# Or permanently via config:
# /etc/NetworkManager/conf.d/99-unmanaged.conf
# [keyfile]
# unmanaged-devices=interface-name:wlan0

3. Create wpa_supplicant config

sudo mkdir -p /etc/wpa_supplicant
sudo tee /etc/wpa_supplicant/wpa_supplicant.conf << 'EOF'
ctrl_interface=/var/run/wpa_supplicant
update_config=1
country=US
EOF
sudo chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf

4. Start wpa_supplicant

# Replace wlan0 with your interface name
sudo wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf

5. Verify wpa_cli works

wpa_cli -i wlan0 status
# Should show: wpa_state=DISCONNECTED (or COMPLETED if connected)

Troubleshooting wpa_supplicant

Problem: wpa_cli fails with "Failed to connect to non-global ctrl_ifname"

This means wpa_supplicant is not running with a control interface for your WiFi device. Common causes:

  1. wpa_supplicant running in D-Bus-only mode (no -i flag):

    # Check how it's running
    pgrep -a wpa_supplicant
    # Bad: /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -u -s
    # Good: /usr/sbin/wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
    

    Fix: Kill and restart with interface flag:

    sudo killall wpa_supplicant
    sudo wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
    
  2. Missing ctrl_interface in config:

    Ensure /etc/wpa_supplicant/wpa_supplicant.conf contains:

    ctrl_interface=/var/run/wpa_supplicant
    
  3. Control socket directory doesn't exist:

    ls -la /var/run/wpa_supplicant/
    # Should show a socket file for your interface
    

Problem: Interface is DOWN

sudo ip link set wlan0 up

Install Playwright browser (for browser automation)

npx playwright install chromium

Claude Code Configuration

Register the MCP server with Claude Code:

claude mcp add wpa-mcp --transport http http://localhost:3000/mcp

Then start a new Claude Code session to use the WiFi tools.

Claude Desktop Configuration

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "wpa-mcp": {
      "url": "http://<HOST_IP>:3000/mcp"
    }
  }
}

Available MCP Tools

WiFi Management

ToolDescription
wifi_scanScan for available networks (returns SSID, signal, security type)
wifi_connectConnect to WPA-PSK or open network
wifi_connect_eapConnect to WPA2-Enterprise/802.1X network (PEAP, TTLS, TLS)
wifi_disconnectDisconnect from current network
wifi_statusGet connection status (wpa_state, ssid, ip_address, EAP info)
wifi_list_networksList saved networks with flags (CURRENT, TEMP-DISABLED)
wifi_forgetRemove a saved network by network_id
wifi_reconnectReconnect using saved configuration

WiFi Diagnostics

ToolDescription
wifi_eap_diagnosticsGet EAP authentication state and decision
wifi_get_debug_logsGet filtered wpa_supplicant logs (eap, state, scan, error)

Browser Automation

ToolDescription
browser_openOpen URL in default browser
browser_run_scriptRun a Playwright automation script
browser_list_scriptsList available scripts

Network Connectivity

ToolDescription
network_pingPing a host
network_check_internetCheck internet connectivity
network_check_captiveDetect captive portal
network_dns_lookupPerform DNS lookup

Playwright Scripts

Scripts are stored in ~/.config/wpa-mcp/scripts/.

Script Format

// ~/.config/wpa-mcp/scripts/my-portal.js
export default async function(page, variables) {
  const { username, password } = variables;

  await page.goto('http://captive-portal.example.com');
  await page.fill('#username', username || '');
  await page.fill('#password', password || '');
  await page.click('button[type="submit"]');

  return 'Login completed';
}

Running Scripts

Via MCP tool:

browser_run_script("my-portal", { username: "guest", password: "wifi123" })

Example Usage

Basic WiFi Connection

User: "Scan for WiFi networks"
Claude: [calls wifi_scan]
→ Lists available networks with signal strength and security type

User: "Connect to 'CoffeeShop' with password 'guest123'"
Claude: [calls wifi_connect with ssid="CoffeeShop", password="guest123"]
→ Connects to the network

WPA2-Enterprise Connection

User: "Connect to corporate WiFi 'CorpNet' with my credentials"
Claude: [calls wifi_connect_eap with ssid="CorpNet", identity="user@corp.com", password="secret"]
→ Connects using PEAP/MSCHAPv2

User: "Connection failed, why?"
Claude: [calls wifi_get_debug_logs with filter="eap"]
→ Shows EAP authentication logs revealing identity rejection or credential failure

Debugging Connection Issues

User: "WiFi keeps disconnecting"
Claude: [calls wifi_get_debug_logs with filter="state"]
→ Shows state transitions: COMPLETED -> DISCONNECTED -> SCANNING

User: "Check EAP diagnostics"
Claude: [calls wifi_eap_diagnostics]
→ Returns: eap_state=IDLE, decision=FAIL (server rejected credentials)

Captive Portal Handling

User: "Check if there's internet"
Claude: [calls network_check_internet]
→ Reports online status and latency

User: "Check for captive portal"
Claude: [calls network_check_captive]
→ Detects if behind a login page

User: "Run the hotel-login script with room 101"
Claude: [calls browser_run_script with script_name="hotel-login", variables={room: "101"}]
→ Executes Playwright script to handle login

Debug Log Filters

The wifi_get_debug_logs tool supports these filters to help diagnose specific issues:

FilterUse CaseWhat It Shows
allFull debuggingAll wpa_supplicant logs
eap802.1X/credential issuesEAP identity, method selection, authentication result
stateConnection flowState transitions (SCANNING → AUTHENTICATING → COMPLETED)
scanNetwork discoveryScan results, BSS information
errorFailuresTimeouts, authentication failures, TEMP-DISABLED events

By default, logs are filtered to show only entries since the last WiFi command, making it easy to correlate actions with results.

API Endpoints

  • POST /mcp - MCP protocol endpoint (Streamable HTTP)
  • GET /health - Health check

License

MIT