Skip to main content
Glama
README.md
# router-mcp

[![npm](https://img.shields.io/npm/v/@subashgautam/router-mcp.svg)](https://www.npmjs.com/package/@subashgautam/router-mcp)

> A **Model Context Protocol (MCP)** server **+ CLI** that lets an AI assistant (Claude, or any MCP client) **monitor and control your WiFi router** — list connected devices, inspect WiFi networks, check WAN status, change SSIDs, reboot, and more.

> **Published as `@subashgautam/router-mcp`.** After a global install the CLI command is just `router-mcp`.

Built on [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk). Designed around **pluggable adapters**, so it can target any router family. It ships with:

- **`openwrt`** — talks to OpenWrt (and most derivatives) over SSH using `ubus` / `uci`.
- **`mock`** — realistic fake data so you can try everything with **no hardware**.

It is also a normal **TypeScript/JavaScript library** you can import.

---

## Features

- 🔌 **MCP server** — exposes router operations as MCP tools to any MCP client.
- 🖥️ **CLI** — drive your router straight from the terminal (`router-mcp devices`, `status`, `wifi`, ...).
- 🧩 **Pluggable adapters** — add a new router backend by implementing one interface.
- 🔒 **Safe by default** — read-only unless you explicitly opt in to writes (`--allow-write`) and raw command execution (`--allow-exec`).
- 📦 **Library + CLI** — `import { OpenWrtAdapter } from "@subashgautam/router-mcp"` or run the binary.

---

## Install

```bash
# Use immediately with no install
npx @subashgautam/router-mcp status --adapter mock

# Or install globally for the CLI (provides the `router-mcp` command)
npm install -g @subashgautam/router-mcp

# Or as a project dependency (library use)
npm install @subashgautam/router-mcp
```

Requires **Node.js >= 18**.

---

## Quick start

### 1. Try it with no hardware (mock adapter)

```bash
npx @subashgautam/router-mcp status   --adapter mock
npx @subashgautam/router-mcp devices  --adapter mock
npx @subashgautam/router-mcp wifi     --adapter mock
```

> Tip: after `npm install -g @subashgautam/router-mcp` you can drop the `npx @subashgautam/` prefix and just run `router-mcp status` etc.

### 2. Point it at a real OpenWrt router

```bash
# Prefer the env var (or --key) so the password isn't visible in the process list:
ROUTER_HOST=192.168.1.1 ROUTER_USER=root ROUTER_PASSWORD='yourpass' npx @subashgautam/router-mcp devices

# --password also works, but see the security note below.
npx @subashgautam/router-mcp devices --host 192.168.1.1 --user root --password 'yourpass'
```

### 3. Use it as an MCP server (Claude Desktop, etc.)

Add to your MCP client config (see [`examples/claude_desktop_config.json`](examples/claude_desktop_config.json)):

```jsonc
{
  "mcpServers": {
    "router": {
      "command": "npx",
      "args": ["-y", "@subashgautam/router-mcp", "serve"],
      "env": {
        "ROUTER_ADAPTER": "openwrt",
        "ROUTER_HOST": "192.168.1.1",
        "ROUTER_USER": "root",
        "ROUTER_PASSWORD": "your-router-password",
        "ROUTER_ALLOW_WRITE": "1"
      }
    }
  }
}
```

Then ask your assistant things like *"which devices are connected to my router?"* or *"rename my 2.4GHz WiFi to HomeNet"*.

---

## MCP tools

| Tool | Permission | Description |
|------|-----------|-------------|
| `router_status` | read | Model, firmware, hostname, uptime, load, memory. |
| `list_devices` | read | Connected/known clients: MAC, IP, hostname, signal, interface. |
| `list_wifi_networks` | read | Configured SSIDs with id, state, channel, band, encryption. |
| `wan_info` | read | WAN/upstream: up state, protocol, public IP, gateway, uptime. |
| `set_wifi` | **write** | Change an SSID's name, password, channel, or enabled state. |
| `reboot_router` | **write** | Reboot the router (requires `confirm: true`). |
| `run_command` | **exec** | Run a raw shell command on the router. |

Write tools appear **only** when the server is started with `--allow-write`; `run_command` only with `--allow-exec`.

---

## CLI reference

```
router-mcp [serve] [options]      Start the MCP server over stdio (default)
router-mcp <command> [options]    Run a command directly against the router

Commands:
  serve            Run the MCP server (stdio). Default when no command given.
  status           Show router model, firmware, uptime, load, memory.
  devices          List connected/known devices.
  wifi             List configured WiFi networks.
  wan              Show WAN/upstream connection info.
  reboot           Reboot the router (needs --allow-write).
  exec "<cmd>"     Run a raw shell command on the router (needs --allow-exec).

Options:
  --adapter <openwrt|mock>   Default: openwrt if --host given, else mock.
  --host --port --user --password --key --wan-iface
  --allow-write  --allow-exec  --json  -h/--help  -v/--version
```

### Environment variables

`ROUTER_ADAPTER`, `ROUTER_HOST`, `ROUTER_PORT`, `ROUTER_USER`, `ROUTER_PASSWORD`, `ROUTER_KEY`, `ROUTER_WAN_IFACE`, `ROUTER_ALLOW_WRITE`, `ROUTER_ALLOW_EXEC`.

---

## Library usage

```ts
import { OpenWrtAdapter, startStdioServer } from "@subashgautam/router-mcp";

// Use an adapter directly
const router = new OpenWrtAdapter({ host: "192.168.1.1", password: "..." });
console.log(await router.getStatus());
console.log(await router.listDevices());
await router.close();

// Or start a full MCP server programmatically
await startStdioServer({ adapter: "openwrt", host: "192.168.1.1", password: "...", allowWrite: true });
```

---

## Writing a custom adapter

Implement the `RouterAdapter` interface and pass an instance to `buildServer`:

```ts
import { buildServer, type RouterAdapter } from "@subashgautam/router-mcp";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

class MyRouterAdapter implements RouterAdapter {
  readonly name = "myrouter";
  async getStatus() { /* ... */ return {}; }
  async listDevices() { return []; }
  async getWifiNetworks() { return []; }
  async getWanInfo() { return {}; }
  async setWifi() { /* ... */ }
  async reboot() { /* ... */ }
}

const server = buildServer({
  adapter: new MyRouterAdapter(),
  config: { allowWrite: true, allowExec: false },
});
await server.connect(new StdioServerTransport());
```

---

## Security notes

- The server is **read-only by default**. Enabling `--allow-write` / `--allow-exec` lets an AI client change settings or run commands on your router — only enable what you need.
- **Avoid `--password` on the command line** — process arguments are world-readable on most systems (`ps aux`, `/proc/<pid>/cmdline`), so the password leaks to other local users. Prefer **key-based SSH auth** (`--key`) or the **`ROUTER_PASSWORD`** environment variable.
- `run_command` is powerful; treat it like giving shell access. It is only available with `--allow-exec`.
- Wifi network ids passed to `set_wifi` are validated against the uci section-name charset (`[A-Za-z0-9_]`) before use, so a malicious id cannot inject shell commands.

---

## License

MIT © SwiftTech

TDQS

A4.2/5.0

Scored across 7 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: listing clients, listing WiFi networks, rebooting, system status, arbitrary commands, modifying WiFi, and WAN info. No overlap or ambiguity.

Naming Consistency4/5

Most tools use verb_noun (list_devices, reboot_router, run_command, set_wifi) while router_status and wan_info use noun_noun. This is a minor inconsistency, but the pattern is still predictable overall.

Tool Count5/5

7 tools is well-scoped for a router management server. Each tool covers a fundamental operation, neither too few nor too many.

Completeness3/5

Covers core router operations (clients, WiFi, reboot, status, WAN), but lacks a create_wifi tool for adding new networks. The run_command provides a fallback, but its restriction and danger limit completeness.

Maintenance

ActivityStale
ResponsivenessNo issues