Skip to content

Releases: salatmaster/keenetic-mcp

v0.3.0

Choose a tag to compare

@salatmaster salatmaster released this 07 Aug 16:13
abb2487

Isolated networks: a guest network, an IoT network, a segment routed through a VPN tunnel, created so the router actually lists them.

The problem this solves

The obvious way to build one is a bridge with an address, NAT and a Wi-Fi binding. It works: clients associate, get addresses, reach the internet, and stay off the home network. It is also invisible. Nothing appears in the segment list, and the network cannot be managed from the web interface at all.

What the router calls a segment is VLAN-backed. Three steps are missing: a GigabitEthernet0/VlanN subinterface, that VLAN trunked over every switch port, and the subinterface bridged in. The router then computes the iseg block itself, and that is what the web interface reads.

New tools

Tool
create_segment the whole sequence in one call: VLAN, bridge, address, NAT, DHCP, and optionally Wi-Fi and a routing policy
list_segments every bridge, with uiVisible per bridge
delete_segment the segment and everything created with it, including the VLAN on every port

Free identifiers are chosen automatically. The result is verified against iseg rather than against the return value, because the router answers a wrong field name with {} and no error. A failure anywhere rolls the whole thing back and names anything it could not undo.

create_segment { "name": "iot", "ssid": "…", "psk": "…" }
create_segment { "name": "vpn", "permit_interfaces": ["Wireguard1"] }

New skill

keenetic-segments carries the same sequence for doing it by hand through rci_call: the discovery reads that find free identifiers, the build, the verification, and the teardown, including the per-port VLAN removal that gets forgotten and leaves the switch full of orphaned VLANs.

Verified on hardware

Built and torn down on a Keenetic Ultra (KN-1811) running 5.1.3.

ports before  access=1 trunk=[2]
ports during  access=1 trunk=[2,3]
ports after   access=1 trunk=[2]

switchport trunk vlan is additive, so trunking every port leaves the home network and any existing segment on those ports untouched. Two things the live run corrected: Bridge0 has an empty iseg because the home network is untagged rather than a VLAN, and is now reported as home instead of as invisible; and a segment with no Wi-Fi does not appear on the "My networks and Wi-Fi" page, because that page lists Wi-Fi networks. Both are documented in the skill.

Safety, unchanged

Nothing is saved. A segment is discarded on reboot until save_config runs, which makes it cheap to build one, look at it, and change your mind. delete_segment refuses the home segment and leaves any routing policy alone, because policies are shared.

Install

Claude Code

/plugin marketplace add salatmaster/keenetic-mcp
/plugin install keenetic@keenetic
npx -y keenetic-mcp init

Codex

codex plugin marketplace add salatmaster/keenetic-mcp
codex plugin add keenetic@keenetic
npx -y keenetic-mcp init

Anything else

{ "mcpServers": { "keenetic": { "command": "npx", "args": ["-y", "keenetic-mcp"] } } }

245 tests. Notes on the API itself are in docs/rci-api.md.

v0.2.2

Choose a tag to compare

@salatmaster salatmaster released this 07 Aug 10:11
0d26827

Two bugs where the server reported success for something it never did, both found and fixed by @dsudomoin against a Giga KN-1012 on KeeneticOS 5.1.1.

Every rci_call POST could silently do nothing

body was declared with no type in the advertised schema, so a client with nothing to go on could serialise it to a string. That string was JSON-encoded a second time on the way out, the router matched no command and answered {}: HTTP 200, no error, nothing done. save_config goes through the same path, so a save could report success having sent nothing.

The schema now describes the shape, and a body that arrives as text is decoded before it goes out. Anything that still is not a command tree is refused rather than sent.

save_config confirmed saves it never checked

unsavedChanges came from fail-safe.unsaved, which tracks the fail-safe rollback timer rather than the saved state. A real router reports it false while a change sits unsaved, so the check passed while the configuration in flash was still the old one, and a reboot would have discarded everything.

It now compares the running checksum against the one the router stamps into the startup-config header. An unreadable startup config answers unsavedChanges: null, and save_config accepts only an explicit false as confirmation, because answering "saved" on a failed read would repeat the same mistake in a new place.

The handshake reported the wrong version

Fixed by @BrainRTP: the version in serverInfo was a literal and had sat at 0.1.0 while the package shipped 0.2.1. It is read from the package at runtime now.

Releases are cut from the tag

No version is written down in the repository any more. package.json carries 0.0.0-dev and the release workflow stamps the tag into it just before publishing, so a release is a tag and nothing else. The plugin manifests carry no version at all, and the plugins pin keenetic-mcp@^0, which tracks the major only.

A checkout reports 0.0.0-dev. KEENETIC_MCP_VERSION, or a .env beside package.json, overrides that for local work.

Also

The keenetic-rci skill now documents the no syntax for deletes, which is the part of the command tree that is hardest to guess: no sits beside the arguments rather than wrapping them, and the object's own name travels in name. Confirmed on 5.1.1 and 5.1.3.

Install

Claude Code

/plugin marketplace add salatmaster/keenetic-mcp
/plugin install keenetic@keenetic
npx -y keenetic-mcp init

Codex

codex plugin marketplace add salatmaster/keenetic-mcp
codex plugin add keenetic@keenetic
npx -y keenetic-mcp init

Anything else

{ "mcpServers": { "keenetic": { "command": "npx", "args": ["-y", "keenetic-mcp"] } } }

217 tests. Notes on the API itself are in docs/rci-api.md.

v0.2.1

Choose a tag to compare

@salatmaster salatmaster released this 07 Aug 03:05

A patch release with one fix: npx keenetic-mcp init asked for the router password without showing a prompt, and echoed what you typed.

The fix

The reader wrote its prompt straight to stdout and then muted readline's private _writeToOutput. In terminal mode readline redraws the current line, which erased the prompt, and muting that internal does not suppress the echo on current Node. So nothing on screen asked for a password, and the password itself was in plain view.

It is now read in raw mode, where the terminal itself stops echoing, with backspace, Ctrl-C and Ctrl-D handled. The readline interface is opened per visible question and closed again, so it never competes with the raw reader for stdin.

If you ran keenetic-mcp init on any earlier release, your router password was displayed as you typed it and is likely still in your terminal scrollback. 0.1.0, 0.1.1 and 0.2.0 are all affected. Clear the scrollback, and change the router password if anyone else can reach that terminal or its logs.

The closing message now names Claude Code as well as Codex, and points at the plugin rather than the bare server, since the plugin brings the skills too.

How it is checked

Neither symptom was reachable from the unit tests: they inject the reader, and piped input takes a different code path in the wizard entirely. Piping into a pty does not help either, because the end of the pipe arrives as Ctrl-D and aborts the first question.

scripts/verify-wizard.exp drives a real terminal instead. It confirms the prompt reaches the screen and the password does not, and it fails on the previous code. A terminal is required, so CI cannot run it; the README says how to run it by hand.

Install

Claude Code

/plugin marketplace add salatmaster/keenetic-mcp
/plugin install keenetic@keenetic
npx -y keenetic-mcp init

Codex

codex plugin marketplace add salatmaster/keenetic-mcp
codex plugin add keenetic@keenetic
npx -y keenetic-mcp init

Anything else

{ "mcpServers": { "keenetic": { "command": "npx", "args": ["-y", "keenetic-mcp"] } } }

Verified against

Keenetic Ultra (KN-1811) on KeeneticOS 5.1.3. 199 tests. Notes on the API itself are in docs/rci-api.md.

v0.2.0

Choose a tag to compare

@salatmaster salatmaster released this 07 Aug 02:44
05e2cb6

First release with a plugin for both agents. One plugin directory serves Claude Code and Codex: they read different manifests but share the same server definition and the same three skills.

Install

Claude Code

/plugin marketplace add salatmaster/keenetic-mcp
/plugin install keenetic@keenetic
npx -y keenetic-mcp init

Codex

codex plugin marketplace add salatmaster/keenetic-mcp
codex plugin add keenetic@keenetic
npx -y keenetic-mcp init

Anything else

{ "mcpServers": { "keenetic": { "command": "npx", "args": ["-y", "keenetic-mcp"] } } }

What is in it

Sixteen tools: eleven that read the router, four that change it, and rci_call for anything the rest do not cover.

Three skills, so the agent knows how the router behaves rather than guessing. keenetic-rci covers the API tree, the paths that return over 100 KB, and how to recover a command's syntax from the router's own configuration. keenetic-safe-changes covers the change workflow and what the router's fail-safe does not protect against. keenetic-troubleshoot is an ordered diagnostic playbook.

npx keenetic-mcp init finds the router from the default gateway, confirms it is a Keenetic, verifies the credentials against it, and stores the password in the system keychain.

Safety

Nothing is saved unless asked: changes apply to the running configuration and are discarded on reboot until save_config runs. A backup is taken before the first change of a session. Every change is read back and compared, because the router accepts some wrong commands silently and changes nothing. Under --read-only the write tools are not registered at all.

Changes since 0.1.1

  • Codex plugin, sharing one directory with the Claude Code plugin
  • the version now lives only in package.json; npm version propagates it to every manifest and to the pin the plugins share
  • releases publish through npm trusted publishing, so no token is stored anywhere

Verified against

Keenetic Ultra (KN-1811) on KeeneticOS 5.1.3. RCI is a standard part of KeeneticOS rather than a feature of high-end models, so this works across the range; the tool set adapts to the components each router reports.

199 tests. Notes on the API itself are in docs/rci-api.md.