Driving RouterOS with centrs
SkillFiles & storageLets your agent read and change settings on MikroTik routers, run commands, move files, and find nearby devices.
Use Driving RouterOS with centrs in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Driving RouterOS with centrs and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Driving RouterOS with centrs skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
About this skill
Use whenever a task touches a real MikroTik RouterOS device or CHR: reading or changing config, running a RouterOS CLI command non-interactively, checking a command is well-formed before sending it, moving files on or off a device, discovering neighbors, or doing the same thing across several router
What this skill tells your AI
The instructions your AI receives, as published by tikoci/routeros-skills in routeros-centrs/SKILL.md and read by Ahel’s review.
What this is for
@tikoci/centrs is a friendly conduit to
RouterOS, not a configuration abstraction. You still speak RouterOS; centrs handles
the parts that are tedious and error-prone to do by hand:
- resolving
<router>to an address, credentials, port, and a protocol; - validating a RouterOS-shaped command before it runs;
- returning the same structured envelope whatever transport carried the call.
Validation and structured diagnostics are the product. Without them it would be a
worse curl.
Reach for centrs when you need to run a RouterOS command, read state, or move a file — from a shell script, a test, or an agent loop.
Don't reach for it when you need RouterOS documentation (use the rosetta MCP
or the routeros-fundamentals skill), or when you need to create the router itself
(use routeros-quickchr-cli from a shell, or routeros-quickchr for the TypeScript
library — centrs consumes a quickchr VM, it does not boot one).
Status:
0.1.xpreview under active development. The repo README publishes preview builds under npm'snexttag, so pin@nextrather than assuminglatesttracks them — confirm withnpm view @tikoci/centrs dist-tags. Expect breaking changes before 1.0 and exercise writes on lab or disposable targets first.docs/MATRIX.mdis the single source of truth for what works today; treat anything not green there as not-yet-shipped.
Install and first call
bunx @tikoci/centrs@next --help # no install
bun add @tikoci/centrs@next # library + local CLI
The offline analyzer needs no router and no credentials — it is the cheapest way to confirm centrs works at all:
bunx @tikoci/centrs@next explain '/ip/address print' --json
The loop: explain → run
This is the pattern centrs exists for. Analyze the command offline, then run it.
# 1. Is this well-formed, and what will it actually do?
centrs explain '/ip/address/add address=192.0.2.1/24 interface=ether2'
# 2. Run it. Writes need --yes when there is no TTY to prompt on.
centrs execute lab --yes '/ip/address/add address=192.0.2.1/24 interface=ether2'
# 3. Read it back as data.
centrs retrieve lab /ip/address --json
explain is offline: it opens no connection, canonicalizes the input, reports
structure and syntax diagnostics, and tells you which centrs command would carry it.
It is also the first thing to run when a command is rejected — see
When a command is rejected.
explain is deliberately conservative: a pass verdict means centrs found nothing
wrong offline, never that RouterOS will accept it. The envelope says so itself with
runtimeAcceptance: "not-proven". Live device probes are designed, not shipped.
Naming a router
Every router-facing command resolves a target. Four ways, in the order you will want them:
| You have | Use |
|---|---|
| A CHR booted by quickchr | --quickchr <name> — resolves host, port, and credentials from the live VM; no credential plumbing at all |
| A router saved in WinBox | the positional <router> — centrs reads ~/.config/tikoci/winbox.cdb (the WinBox address book is the device registry; centrs keeps no store of its own) |
| Neither | --host / --port / --username / --password, or CENTRS_USERNAME / CENTRS_PASSWORD |
| Nothing yet | centrs discover over MNDP, --save to write found neighbors into the CDB |
--quickchr is exclusive of positional targets and of --host/--port/--username/
--password; pick one mechanism per call.
Fan out instead of looping
--quickchr repeats, and the CDB selectors fan out too. Prefer these to a shell loop —
one envelope, bounded concurrency, per-target errors that do not abort the rest:
centrs retrieve --quickchr sun --quickchr earth --quickchr comet /system/resource --json
centrs execute --group edge --yes '/system/ntp/client/set enabled=yes'
centrs retrieve --all /system/resource --json
Fan-out selectors: --group <name>, --all, --where <attr>=<value>, --near,
--bbox. --default is a target selector too, but it picks the single reserved
__default__ record rather than fanning out.
The command map
Twelve commands. The three obvious ones are not the whole surface — explain, api,
and discover are the ones most callers never find.
| Command | Purpose | State |
|---|---|---|
retrieve <router> <path> | Read RouterOS state | shipped (REST, native API) |
execute <router> '<cli>' | Run a RouterOS CLI-shaped read/write | shipped (REST, native API, SSH, MAC-Telnet) |
api <router> <endpoint> | Structured passthrough, gh api style: -X PUT, -f k=v, --query, --stream | shipped (REST, native API) |
explain '<input>' | Offline analysis of a RouterOS command | shipped offline; live probes designed |
transfer <router> upload|download|list|remove|mkdir|copy | Device files | shipped (REST, native API, SFTP) |
terminal <router> | Interactive console | shipped (SSH, MAC-Telnet) |
discover | MNDP neighbor discovery, --save into the CDB | shipped |
devices | The CDB device registry, and the atomic write layer every CDB mutation routes through — including discover --save | shipped |
settings | centrs's own preferences (centrs.env) | shipped |
mcp | Scoped stdio MCP server, CDB-gated | shipped |
btest | MikroTik bandwidth test, client or server | shipped |
check | Reachability + health battery | designed only — not implemented |
retrieve vs execute vs api: retrieve reads a menu; api is the structured
operation surface (it can write); execute takes a literal RouterOS CLI string and is
the only one that reaches SSH and MAC-Telnet. There is no update command — CLI-shaped
writes ride execute.
Read the envelope, not the text
Every call returns one shape, whatever the transport (the one exception is
api --raw, which deliberately strips the envelope and emits bare RouterOS JSON):
{
"ok": true,
"data": [ /* the payload */ ],
"warnings": [], // always present; non-fatal anomalies about this result
"tips": [], // always present; advice that is NOT a problem
"meta": {
"target": {}, // resolved target + where each field came from
"via": "rest-api", // the protocol actually chosen
"settings": {}, // which setting won, and from which source
"validation": {}, // validator name + result, if validation ran
"operation": { "objectCount": 3 }
}
}
Pass --json (or --format json). This is the single most-missed thing about
centrs. retrieve and execute default to --format text, and their text output
renders data as pretty-printed JSON — so piping the default straight into a JSON
parser appears to work while silently discarding warnings, tips, and all of
meta. (api already defaults to JSON.)
Two consequences worth internalizing:
-
meta.operation.objectCountis the reliable row count.dataitself is shape-unstable today: zero rows come back as an empty object, one row as a bare object, and N rows as an array (centrs#360). All three need handling, and the empty object is the one that bites — it is truthy, so the obviousd ? [d] : []invents a row that does not exist, exactly during the failover or empty-menu read you were measuring. Take the count frommeta, and normalize with something that excludes it:const rows = Array.isArray(d) ? d : d && typeof d === "object" && Object.keys(d).length > 0 ? [d] : []; -
tipsandwarningsare separate channels on purpose. A tip is explicitly not a problem; do not treat a non-emptytipsarray as a failure.
Writing
Write-shaped commands are gated. With a TTY, centrs prompts; without one it refuses:
$ centrs execute lab '/ip/address/add address=192.0.2.1/24 interface=ether2'
[usage/confirmation-required] Write-shaped RouterOS execute commands require explicit confirmation.
Fix: Pass `--yes` in non-interactive automation, or answer `yes` at the TTY prompt after reviewing the command.
So: a non-interactive execute or api write needs --yes, as does a mutating
transfer fan-out across several routers. Overwriting an existing file is a separate
gate — transfer --force / --overwrite, not --yes. Never disable validation to make
a write succeed: validation is the product, and --no-validate should be a deliberate,
explained choice, not a workaround.
Files
centrs transfer <router> upload ./routeros-7.24.4.npk
centrs transfer <router> list
centrs transfer <router> download flash/backup.rsc ./backup.rsc
RouterOS's /file contents write caps at 60 KB, so REST and native API cannot
carry a large upload. With no --via pinned, centrs already notices the size and
auto-selects SFTP — you do not need to pick the transport. On a device with no
SSH key installed that auto-hop can fail on authentication rather than on size: the
SSH clients run with BatchMode=yes, so an empty or password-only credential is not
usable there even though REST accepts it.
When a command is rejected
Validation is two stages, and which one rejected you is the whole diagnosis:
- Offline — the same analyzer
explainuses runs first, with no connection. A syntax fault is refused here with the offending byte span, and its remediation tells you to runcentrs explainto see it in context. No round trip happens. - Device —
:parseplus/console/inspecton the router, for the semantic half offline analysis cannot decide.
--validate=false disables both. A clean offline pass is necessary, never sufficient.
The device stage is where the error text is thin, and the failure mode is worth knowing because it costs agents real time:
$ centrs execute lab '/container/print'
[validation/syntax] RouterOS rejected the command syntax while parsing it.
Fix: Fix the RouterOS CLI syntax (quotes, brackets, attribute form), then retry.
That remediation is often wrong. validation/syntax is what you get when the path
does not exist for any reason — including a menu whose package is not installed or
whose device-mode feature is off
(centrs#361). Re-quoting a command that
has no quoting problem is an infinite loop. When you see it:
- Run
centrs explain '<the same command>'. If it passes offline, the input is well-formed and the problem is the device, not the string — and since stage 1 already ran the same analyzer, a rejection you received from a router is by definition one offline analysis let through. Re-quoting cannot help. - Check the device —
centrs retrieve <router> /system/package --json, thencentrs retrieve <router> /system/device-mode --json. A missingcontainer,zerotier, orwirelesspackage is the usual answer. - Only then suspect the syntax — and use
explain's diagnostics rather than guessing.
Re-run with --verbose to get the error context, which carries the device's own
:parse output; the default two-line render drops it, along with the per-code details
URL and the (line N column M) position
(centrs#362). Every error code has a
page at https://tikoci.github.io/centrs/errors/<code>.
Known rough edges
Behaviors to plan around rather than debug from scratch. Each links to the tracking issue — if you hit one, add evidence there rather than working around it silently.
- A hang can be the success path.
/system/device-mode/update container=yesblocks by design while RouterOS waits for a hard power-cycle to confirm. centrs reports that as a plain timeout on every transport (centrs#363). For a CHR, set device-mode at VM creation and let quickchr do the power-cycle. The same applies to/system/reboot, package installs, and unbounded/ping. - No wait primitive. There is no
--wait/--until; readiness and convergence polling is the caller's job today (centrs#364). Poll a cheap read (retrieve <router> /system/resource) against a wall-clock deadline, and do not silence its errors — "not converged" and "centrs failed" must stay distinguishable. - Output can contain secrets. centrs redacts credentials it holds; it has no
notion of secrets RouterOS returns
(centrs#359).
/file print detailcan inline private keys and API tokens, and/export show-sensitive=yesis one word from the safe default. Prefer targeted reads over broad dumps, especially through MCP, where the output leaves the machine. - REST timeouts cap at 60 s.
--timeoutabove that is rejected onrest-api. checkis not implemented. It isdesignedin the matrix; do not build on it.
Authoritative docs & related skills
- centrs repo:
README ·
docs/CLI.md(generated full flag reference) ·docs/CONSTITUTION.md(envelope, error model, protocol selection) ·docs/MATRIX.md(what actually works) ·commands/(per-command contract and worked examples) - routeros-quickchr-cli / routeros-quickchr — boot the CHR that
--quickchrthen targets: the-cliskill for the shell path, the other for TypeScript harnesses. The pair is the normal grounding loop: quickchr creates the router, centrs drives it. - routeros-fundamentals / routeros-scripting — what to actually say to RouterOS once centrs can reach it.
- routeros-syntax-inspection —
/console/inspectand:parse, the machinery behind centrs's validation gate. - routeros-mac-telnet / routeros-mndp — the L2 protocols behind
execute --via mac-telnetanddiscover.
This skill is a starting point, not a manual.
docs/CLI.mdandcommands/<name>/in the repo are authoritative and versioned; when they disagree with this file, they win — and the disagreement is worth reporting at https://github.com/tikoci/centrs/issues.
Signals
- GitHub stars
- 67
- Forks
- 15
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
routeros-centrs- Source
- github.com/tikoci/routeros-skills
github.com/tikoci/routeros-skills
Related picks
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScriptcron-manager
Skill · countbot-ai
The pick for Croncron-packs
Skill · profbernardoj
The pick for Cronassemblyai-webhooks-events
Skill · jeremylongshore
The pick for Webhookswebhooks
Skill · nahid-sparktales
The pick for Webhooks