keenetic-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| MCP_PORT | No | Listening port | 9584 |
| MCP_WATCH | No | Event watcher on or off | true |
| MCP_SECRET | No | Secret token in the URL path | changeme |
| KEENETIC_HOST | No | Router host URL for RCI | http://192.168.1.1 |
| KEENETIC_PASS | Yes | Router password for RCI | |
| KEENETIC_USER | No | Router username for RCI | admin |
| BACKUP_ENABLED | No | Scheduled router config backup enabled | false |
| MCP_HTTP_TOOLS | No | Plain-HTTP tool route on or off | true |
| BACKUP_SCHEDULE | No | Scheduled router config backup, in cron format | 0 11 * * 0 |
| MCP_WATCH_RULES | No | Event watcher rules file | watch_rules.json |
| BACKUP_RSYNC_KEY | No | rsync-over-SSH destination key | |
| BACKUP_MCP_CONFIG | No | Also back up .env, watch_rules.json and the init script | true |
| BACKUP_RSYNC_HOST | No | rsync-over-SSH destination host | |
| BACKUP_RSYNC_PATH | No | rsync-over-SSH destination path | |
| BACKUP_RSYNC_USER | No | rsync-over-SSH destination user | |
| MCP_PROTECTED_PORTS | No | External ports the write tools must never forward or remove | |
| MCP_HTTP_TOOL_ALLOWLIST | No | State-changing tools allowed over that route | |
| MCP_PROTECTED_UPSTREAMS | No | host:port upstreams the write tools must never point at | |
| MCP_PROTECTED_PROXY_NAMES | No | KeenDNS proxy names the write tools must never change |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_system_infoA | Router firmware, model, uptime, CPU load and memory, plus this server's version and boot time. Internet connection and external IP are in get_internet_status. |
| get_clientsA | Get list of connected clients (devices) in the network. Each client includes a 'node' field (controller/extender) indicating which mesh node it is connected to. |
| get_unregistered_clientsA | Get list of active but unregistered (unknown) devices in the network |
| get_dhcp_leasesA | Devices holding a lease from the DHCP pool, with expiry time. Fixed reservations are not leases: see get_dhcp_static. |
| get_interfacesA | Get network interfaces status and traffic stats |
| get_logA | Get system log entries with timestamps. Supports an optional time window via since/until ('HH:MM', 'HH:MM:SS' or 'Jul 24 08:00'). For lines about one device (by MAC, IP or name) use get_log_by_device. |
| get_log_by_deviceA | Router log lines about one device: the given MAC, IP or name is expanded to all of that device's known MACs, IPs and names. No time window; for the whole log or since/until use get_log. |
| get_wifiB | Get WiFi radio status: channel, bandwidth, bitrate, temperature, connected stations count |
| get_wifi_stationsA | Get currently associated WiFi stations with signal strength, traffic, device name and mesh node (controller/extender) |
| get_trafficA | Get traffic summary for all active network interfaces (rx/tx bytes) |
| get_internet_statusA | Internet connections that are up: address (the external IP), uptime, default-route flag, priority. Firmware, load and uptime of the router itself are in get_system_info. |
| get_site_surveyB | Scan and list nearby WiFi networks |
| get_channel_analysisA | Analyze WiFi channel congestion and recommend the least busy channel |
| get_vpn_statusA | Get status of all VPN interfaces (WireGuard, IPsec, L2TP, PPTP) |
| get_web_accessA | Web apps published via KeenDNS as the router serves them now (generated nginx config). Configured mappings with their upstreams are in get_keendns_mappings. |
| run_pingB | Ping a host from the router and return latency and packet loss |
| register_clientC | Register a device by MAC address, assign a name and optionally a static IP |
| update_clientB | Update name or static IP of a registered device |
| block_clientA | Block a device by MAC address. An unregistered device is registered as 'Blocked Device' first. Undo with unblock_client. |
| unblock_clientA | Unblock a device blocked by block_client, by MAC address. |
| get_mesh_nodesA | Get Mesh Wi-Fi system nodes: controller and extenders with client count, firmware, uptime and connection speed |
| get_extender_logA | Get system log from mesh extender(s). Extenders are discovered automatically. If extender_ip is not specified, fetches logs from all active extenders. |
| rebootA | Reboot the router. Takes effect at once and cannot be cancelled; this server is unreachable until the router has booted. |
| backup_configA | Manually trigger a router config backup right now |
| backup_mcp_configA | Back up this server's own files that no release or git pull restores (.env, watch_rules.json, the Entware init script) to the NAS. Refreshes the mcp-config/ mirror every run and writes a dated snapshot only when the content changed. Runs synchronously and reports what happened, including a round-trip md5 check of the mirror. Staging is in /tmp (RAM): nothing is written to the USB stick. |
| dump_logA | Snapshot the current router log and rsync it to the NAS backup path (RAM-only staging, no flash writes). Useful to preserve the log before a reboot. |
| rci_queryA | Raw READ-ONLY query against the router's RCI tree. Performs GET /rci/show/ - it cannot write, because writing requires a POST body and this tool never sends one. Use it to explore state that has no dedicated tool yet. Useful paths: 'clock', 'schedule', 'dns-proxy', 'media', 'ntp', 'ip/hotspot', 'interface/GigabitEthernet1', 'components', 'ndns', 'update'. Blacklisted: running-config (use get_config), crypto, ppp, user. |
| get_configA | Read the router's running-config with an optional regex filter. Secrets (md5/nthash/psk/password/private-key) are masked unless include_secrets is true. Examples: filter='ip static' for port forwarding, 'access-list' for firewall, 'ip dhcp host' for static reservations, 'ip http proxy' for KeenDNS. |
| get_config_stateA | Parsed show/last-change: when the config was last touched (date given both in MSK and UTC), which agent and user touched it, and the checksum - the only reliable signal that a save has actually landed on disk. The raw fail-safe block is included as-is, but its 'unsaved' field is NOT a save indicator - it can read false while a save is still in flight. Compare 'checksum' across two calls instead. |
| diff_saved_configA | Diff running-config against startup-config, both read via RCI - a direct, checksum-independent answer to 'what is not saved yet'. Returns only_in_running and only_in_startup line lists; both empty means fully saved. Secrets are masked on both sides before comparing. Not wired into the write tools - call it on demand, not after every write. |
| get_port_forwardingA | List port forwarding / static NAT rules ('ip static') from running-config |
| get_firewall_rulesA | List firewall rules: access-lists with their entries, ip firewall settings and interface access-groups |
| get_dhcp_staticA | List static DHCP reservations ('ip dhcp host'). Unlike get_dhcp_leases, which only shows dynamic pool leases, this shows fixed bindings. |
| get_keendns_mappingsA | KeenDNS / web-access mappings from running-config ('ip http proxy'). Complements get_web_access, which reads the generated nginx config instead. |
| get_mediaA | Storage overview: internal flash and USB drives with partition UUID, label, filesystem, state, free space and which subsystem uses them (e.g. opkg). Use it to check whether the Entware drive is healthy. |
| get_opkg_statusA | Entware/OPKG state: which drive is bound, the initrc path, and whether /opt is actually mounted. If opt_mounted is false, keenetic-mcp itself is running on borrowed time. |
| list_backupsA | List config backup files already present on the NAS (rsync --list-only). Confirms that scheduled backups actually arrived. |
| get_dns_proxyA | DNS proxy status: upstream resolvers (with DoT SNI), the static A/AAAA records the proxy serves (parsed into domain/address), and the 'ip host' config tree that set_dns_host/remove_dns_host write to. A missing proxy-status block is reported as an error, not as an empty list. |
| get_scheduleA | List router schedules (e.g. the firmware auto-update window) with name, weekday/time actions and seconds until the next fire. Answers with an explicit error, never an empty list, when the schedule trees cannot be read - 'no window is configured' and 'I could not read it' must not look alike to a caller deciding whether it may reboot the router. |
| set_port_forwardingA | Create or update a port forwarding rule ('ip static'). The target is addressed by MAC: pass to_host as a MAC, or as an IP that belongs to a registered host (it is resolved, and refused if unknown). dry_run is TRUE by default - it returns the payload without sending it. A real write is saved to startup-config and verified by re-reading the tree. Protected ports (this server's own and MCP_PROTECTED_PORTS) are refused in code. |
| remove_port_forwardingA | Delete a port forwarding rule, selected by index (from get_port_forwarding) or by port. Refuses ambiguous matches and protected ports. dry_run is TRUE by default. Re-create with set_port_forwarding. |
| set_keendns_mappingA | Create or update a KeenDNS web-access mapping ('ip http proxy'): name -> upstream host:port, published on the ndns domain with ssl redirect. Protected names (this server's own and MCP_PROTECTED_PROXY_NAMES) are refused in code. dry_run is TRUE by default. |
| remove_keendns_mappingA | Delete a KeenDNS mapping by name. Protected names are refused. dry_run is TRUE by default. Re-create with set_keendns_mapping. |
| set_dhcp_hostA | Create or update a static DHCP reservation ('ip dhcp host'). Refuses an IP already reserved for a different MAC. The device NAME lives in the known-host tree - use register_client/update_client for that. dry_run is TRUE by default. |
| remove_dhcp_hostA | Delete a static DHCP reservation by MAC. dry_run is TRUE by default. Re-create with set_dhcp_host. |
| set_dns_hostA | Create a static DNS record ('ip host ') served by the router's own DNS proxy - the LAN half of a split-horizon setup, where the same name must resolve to an internal reverse proxy inside the network and to the WAN address outside it. A name that already resolves to a different address is refused rather than extended: 'ip host' accepts several addresses per name and would round-robin it. Remove the old record first. dry_run is TRUE by default; a real write is saved to startup-config and verified by re-reading the tree. |
| remove_dns_hostA | Delete a static DNS record ('no ip host '). The address is looked up in the config tree, because the router's removal form needs both the name and the address; pass it explicitly only to disambiguate a name that holds several. dry_run is TRUE by default. Re-create with set_dns_host. |
| get_watch_statusA | Watcher status: whether the background event watcher is running, which rules file it read, the state of its own RCI session, and per rule - source, poll interval, cooldown, seconds to the next poll, match/sent/failed counters and the last delivery error. Use it to check that a rule is alive without waiting for the event it watches for. |
| test_watch_ruleA | Render a watcher rule's outbound HTTP call with sample event values, so the URL, headers and body can be inspected before a real event fires. Credentials in the rendered view are masked; the call itself, when sent, uses the real values. dry_run is TRUE by default and sends nothing; dry_run=false performs the call for real, which is how to prove the receiver is reachable from the router. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 49 tools
The descriptions actively pre-empt confusion, e.g. get_dhcp_static vs get_dhcp_leases, get_keendns_mappings vs get_web_access, and get_log vs get_log_by_device are explicitly contrasted. A few boundaries are softer (get_config vs rci_query vs get_config_state, get_traffic vs get_interfaces), but each tool still has a distinguishable target.
A clear verb_noun snake_case convention dominates (get_*, set_*, remove_*, backup_*, register_client, block_client). Minor outliers like dump_log, reboot, rci_query and diff_saved_config are still readable and domain-appropriate, so the pattern remains predictable.
49 tools is heavy for a single-router management server, and several read tools (get_config, rci_query, multiple log getters) stack on overlapping ground. Many operations earn their place individually, but the surface is large enough that an agent must scan a long list to pick.
Coverage is broad: full lifecycle for DHCP reservations, DNS hosts, port forwarding and KeenDNS mappings, plus logs, backups, mesh, WiFi, VPN status and a read-only escape hatch (rci_query). Gaps are mostly deliberate read-only areas (firewall rules, VPN configuration) rather than dead ends.