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 | Get router system info: version, uptime, CPU, memory |
| 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_leasesB | Get list of devices with active DHCP leases including expiry time |
| 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'). |
| get_log_by_deviceA | Get system log entries filtered by device MAC address, IP address or name |
| 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_statusB | Get internet connection status and external IP |
| 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_accessB | Get list of web applications exposed to the internet via Keenetic DDNS |
| 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_clientB | Block a registered client by MAC address |
| unblock_clientA | Unblock a previously blocked 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. |
| rebootC | Reboot the router |
| backup_configA | Manually trigger a router config backup right now |
| backup_mcp_configA | Back up the keenetic-mcp files that git cannot restore (.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 as CLI text (the /ci/ endpoints, outside /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. Ports serving MCP endpoints 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. |
| 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 (the MCP servers and Home Assistant) 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. |
| 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. |
| 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. |
| 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
Most tools target distinct resources or actions, and descriptions actively clarify subtle overlaps such as get_dhcp_static vs get_dhcp_leases, get_config vs rci_query, and get_keendns_mappings vs get_web_access. A few boundaries remain potentially confusing, especially around config-state reads, DNS proxy/host reads, and the many client/device listing tools, but an agent can usually disambiguate from the descriptions.
The set is overwhelmingly snake_case with predictable verb_noun or verb_noun_noun forms such as get_clients, set_port_forwarding, remove_dns_host, and backup_config. Minor deviations like reboot, rci_query, and test_watch_rule keep it from being perfectly uniform, but the pattern is still easy to follow.
49 tools is heavy for a single router-management server and exceeds the range where each tool clearly earns its place. Many read-only monitoring tools could be consolidated or grouped, making the surface harder to scan despite the domain being broad.
The server covers a strong read surface and has CRUD for port forwarding, KeenDNS mappings, DHCP reservations, DNS hosts, and client registration/blocking, plus backups. However, notable write gaps remain: firewall rules are readable but not editable, and WiFi, VPN, schedule, firmware update, and package-management configuration lack dedicated write tools.