$npx -y skills add tikoci/routeros-skills --skill routeros-command-treeRouterOS command tree introspection via /console/inspect API. Use when: building tools that parse RouterOS commands, generating API schemas from RouterOS, working with /console/inspect, mapping CLI commands to REST verbs, traversing the RouterOS command hierarchy, or when the use
| 1 | # RouterOS Command Tree & /console/inspect |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | RouterOS organizes all configuration and commands in a **hierarchical tree**. Every path in the CLI |
| 6 | (like `/ip/address/add`) corresponds to a node in this tree. The `/console/inspect` REST endpoint |
| 7 | lets you **programmatically explore the entire tree** — this is how tools like `restraml` (RAML/OpenAPI |
| 8 | schema generator) and `rosetta` (MCP command lookup) build their databases. |
| 9 | |
| 10 | ## The Command Tree Structure |
| 11 | |
| 12 | RouterOS's command hierarchy has four node types: |
| 13 | |
| 14 | | Node Type | Meaning | Example | |
| 15 | |---|---|---| |
| 16 | | `dir` | Directory — contains child paths | `/ip`, `/system` | |
| 17 | | `path` | Path — a navigable level (often has commands) | `/ip/address`, `/interface/bridge` | |
| 18 | | `cmd` | Command — an executable action | `add`, `set`, `print`, `remove`, `get`, `export` | |
| 19 | | `arg` | Argument — a parameter to a command | `address=`, `interface=`, `disabled=` | |
| 20 | |
| 21 | ### Tree Example |
| 22 | |
| 23 | ```text |
| 24 | / (root dir) |
| 25 | ├── ip/ (dir) |
| 26 | │ ├── address/ (path) |
| 27 | │ │ ├── add (cmd) |
| 28 | │ │ │ ├── address (arg) — "IP address" |
| 29 | │ │ │ ├── interface (arg) — "Interface name" |
| 30 | │ │ │ └── disabled (arg) — "yes | no" |
| 31 | │ │ ├── set (cmd) |
| 32 | │ │ ├── remove (cmd) |
| 33 | │ │ ├── get (cmd) |
| 34 | │ │ ├── print (cmd) |
| 35 | │ │ └── export (cmd) |
| 36 | │ ├── route/ (path) |
| 37 | │ │ └── ... |
| 38 | │ └── dns/ (path) |
| 39 | │ ├── set (cmd) |
| 40 | │ ├── cache/ (path) |
| 41 | │ │ ├── print (cmd) |
| 42 | │ │ └── flush (cmd) |
| 43 | │ └── ... |
| 44 | ├── interface/ (dir) |
| 45 | │ └── ... |
| 46 | ├── system/ (dir) |
| 47 | │ └── ... |
| 48 | └── ... |
| 49 | ``` |
| 50 | |
| 51 | ## /console/inspect API |
| 52 | |
| 53 | ### Endpoint |
| 54 | |
| 55 | ```text |
| 56 | POST /rest/console/inspect |
| 57 | ``` |
| 58 | |
| 59 | Requires basic authentication. Available on all RouterOS 7.x versions. |
| 60 | |
| 61 | ### Request Types |
| 62 | |
| 63 | | Request | Purpose | Returns | |
| 64 | |---|---|---| |
| 65 | | `child` | List children of a path | Array of `{type: "child", name, "node-type"}` | |
| 66 | | `syntax` | Get help text for a node | Array of `{type: "syntax", text}` | |
| 67 | | `highlight` | Syntax highlighting data | Per-byte token classes — see the `routeros-syntax-inspection` skill | |
| 68 | | `completion` | Tab-completion suggestions | Completion candidates — see `routeros-syntax-inspection` for validity checking | |
| 69 | |
| 70 | ### Listing Children |
| 71 | |
| 72 | ```typescript |
| 73 | // List children of /ip |
| 74 | const children = await fetch(`${base}/console/inspect`, { |
| 75 | method: "POST", |
| 76 | headers: { ...authHeaders, "Content-Type": "application/json" }, |
| 77 | body: JSON.stringify({ |
| 78 | request: "child", |
| 79 | path: "ip", |
| 80 | }), |
| 81 | }).then(r => r.json()); |
| 82 | |
| 83 | // Response: |
| 84 | // [ |
| 85 | // { "type": "child", "name": "address", "node-type": "path" }, |
| 86 | // { "type": "child", "name": "arp", "node-type": "path" }, |
| 87 | // { "type": "child", "name": "cloud", "node-type": "path" }, |
| 88 | // { "type": "child", "name": "dhcp-client", "node-type": "path" }, |
| 89 | // { "type": "child", "name": "dns", "node-type": "path" }, |
| 90 | // { "type": "child", "name": "route", "node-type": "path" }, |
| 91 | // ... |
| 92 | // ] |
| 93 | ``` |
| 94 | |
| 95 | ### Getting Syntax Help |
| 96 | |
| 97 | ```typescript |
| 98 | // Get description for /ip/address/add → address argument |
| 99 | const syntax = await fetch(`${base}/console/inspect`, { |
| 100 | method: "POST", |
| 101 | headers: { ...authHeaders, "Content-Type": "application/json" }, |
| 102 | body: JSON.stringify({ |
| 103 | request: "syntax", |
| 104 | path: "ip,address,add,address", // comma-separated path |
| 105 | }), |
| 106 | }).then(r => r.json()); |
| 107 | |
| 108 | // Response: |
| 109 | // [{ "type": "syntax", "text": "IP address" }] |
| 110 | ``` |
| 111 | |
| 112 | ### Path Format |
| 113 | |
| 114 | The `path` field uses **comma-separated** segments (not slashes): |
| 115 | |
| 116 | - Root: `""` (empty string) |
| 117 | - `/ip`: `"ip"` |
| 118 | - `/ip/address`: `"ip,address"` |
| 119 | - `/ip/address/add`: `"ip,address,add"` |
| 120 | - `/ip/address/add → address arg`: `"ip,address,add,address"` |
| 121 | |
| 122 | When using the JavaScript `Array.toString()` method, this comma-separated format is produced |
| 123 | naturally from an array: `["ip", "address", "add"].toString()` → `"ip,address,add"`. |
| 124 | |
| 125 | ## Tree Traversal Pattern |
| 126 | |
| 127 | To walk the entire tree recursively: |
| 128 | |
| 129 | ```typescript |
| 130 | async function walkTree(path = [], tree = {}) { |
| 131 | const children = await fetchInspect("child", path.toString()); |
| 132 | |
| 133 | for (const child of children) { |
| 134 | if (child.type !== "child") continue; |
| 135 | |
| 136 | const childPath = [...path, child.name]; |
| 137 | tree[child.name] = { _type: child["node-type"] }; |
| 138 | |
| 139 | // For args, fetch the syntax description — but NOT inside dangerous subtrees |
| 140 | if (child["node-type"] === "arg") { |
| 141 | if (DANGEROUS_PATHS.some(p => childPath.includes(p))) continue; |
| 142 | |
| 143 | const syntax = await fetchInspect("syntax", childPath.toString()); |
| 144 | if (syntax.length === 1 && syntax[0].text.length > 0) { |
| 145 | tree[child.name].desc = syntax[0].text; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // Recurse into this child (child enumeration is saf |