Marketplace routeros-command-tree
RouterOS 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 user mentions inspect, command tree, RAML, or OpenAPI generation for RouterOS.
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tikoci/routeros-command-tree" ~/.claude/skills/aiskillstore-marketplace-routeros-command-tree && rm -rf "$T"
skills/tikoci/routeros-command-tree/SKILL.mdRouterOS Command Tree & /console/inspect
Overview
RouterOS organizes all configuration and commands in a hierarchical tree. Every path in the CLI (like
/ip/address/add) corresponds to a node in this tree. The /console/inspect REST endpoint
lets you programmatically explore the entire tree — this is how tools like restraml (RAML/OpenAPI
schema generator) and rosetta (MCP command lookup) build their databases.
The Command Tree Structure
RouterOS's command hierarchy has four node types:
| Node Type | Meaning | Example |
|---|---|---|
| Directory — contains child paths | , |
| Path — a navigable level (often has commands) | , |
| Command — an executable action | , , , , , |
| Argument — a parameter to a command | , , |
Tree Example
/ (root dir) ├── ip/ (dir) │ ├── address/ (path) │ │ ├── add (cmd) │ │ │ ├── address (arg) — "IP address" │ │ │ ├── interface (arg) — "Interface name" │ │ │ └── disabled (arg) — "yes | no" │ │ ├── set (cmd) │ │ ├── remove (cmd) │ │ ├── get (cmd) │ │ ├── print (cmd) │ │ └── export (cmd) │ ├── route/ (path) │ │ └── ... │ └── dns/ (path) │ ├── set (cmd) │ ├── cache/ (path) │ │ ├── print (cmd) │ │ └── flush (cmd) │ └── ... ├── interface/ (dir) │ └── ... ├── system/ (dir) │ └── ... └── ...
/console/inspect API
Endpoint
POST /rest/console/inspect
Requires basic authentication. Available on all RouterOS 7.x versions.
Request Types
| Request | Purpose | Returns |
|---|---|---|
| List children of a path | Array of |
| Get help text for a node | Array of |
| Syntax highlighting data | Tokenized output (rarely used) |
| Tab-completion suggestions | Completion candidates |
Listing Children
// List children of /ip const children = await fetch(`${base}/console/inspect`, { method: "POST", headers: { ...authHeaders, "Content-Type": "application/json" }, body: JSON.stringify({ request: "child", path: "ip", }), }).then(r => r.json()); // Response: // [ // { "type": "child", "name": "address", "node-type": "path" }, // { "type": "child", "name": "arp", "node-type": "path" }, // { "type": "child", "name": "cloud", "node-type": "path" }, // { "type": "child", "name": "dhcp-client", "node-type": "path" }, // { "type": "child", "name": "dns", "node-type": "path" }, // { "type": "child", "name": "route", "node-type": "path" }, // ... // ]
Getting Syntax Help
// Get description for /ip/address/add → address argument const syntax = await fetch(`${base}/console/inspect`, { method: "POST", headers: { ...authHeaders, "Content-Type": "application/json" }, body: JSON.stringify({ request: "syntax", path: "ip,address,add,address", // comma-separated path }), }).then(r => r.json()); // Response: // [{ "type": "syntax", "text": "IP address" }]
Path Format
The
path field uses comma-separated segments (not slashes):
- Root:
(empty string)""
:/ip"ip"
:/ip/address"ip,address"
:/ip/address/add"ip,address,add"
:/ip/address/add → address arg"ip,address,add,address"
When using the JavaScript
Array.toString() method, this comma-separated format is produced
naturally from an array: ["ip", "address", "add"].toString() → "ip,address,add".
Tree Traversal Pattern
To walk the entire tree recursively:
async function walkTree(path = [], tree = {}) { const children = await fetchInspect("child", path.toString()); for (const child of children) { if (child.type !== "child") continue; const childPath = [...path, child.name]; tree[child.name] = { _type: child["node-type"] }; // For args, fetch the syntax description — but NOT inside dangerous subtrees if (child["node-type"] === "arg") { if (DANGEROUS_PATHS.some(p => childPath.includes(p))) continue; const syntax = await fetchInspect("syntax", childPath.toString()); if (syntax.length === 1 && syntax[0].text.length > 0) { tree[child.name].desc = syntax[0].text; } } // Recurse into this child (child enumeration is safe even in dangerous subtrees) await walkTree(childPath, tree[child.name]); } return tree; }
Dangerous Paths — Must Skip
These path segments crash the RouterOS REST server when their
arg nodes are queried
for syntax via /console/inspect. Always skip syntax lookups for args inside subtrees
containing any of these names:
where, do, else, rule, command, on-error
These are RouterOS scripting constructs. Specifically,
on fetchSyntax()
node-types
within these subtrees terminates the HTTP server process. Enumerating children (arg
child request)
is safe even inside these paths — only the syntax/description lookup for arguments crashes.
The conservative approach (used in the example above) skips the entire arg when any ancestor matches a dangerous path. The actual
rest2raml.js implementation matches this pattern.
const DANGEROUS_PATHS = ["where", "do", "else", "rule", "command", "on-error"];
CLI Command → REST Verb Mapping
RouterOS CLI commands map to HTTP verbs in the REST API:
| CLI Command | HTTP Verb | REST URL Pattern | Notes |
|---|---|---|---|
(print) | | | Returns array of all entries |
(single) | | | Single entry by ID |
| | | Creates new entry (not POST!) |
| | | Updates existing entry |
| | | Deletes entry by ID |
| | | Action-style (also works as GET) |
| Other commands | | | Action — reboot, flush, etc. |
Key insight: REST
PUT = create, PATCH = update. This is the opposite of many REST API conventions where PUT is idempotent update and POST is create.
RAML/OpenAPI Schema Generation
When generating API schemas from the command tree:
- Walk the tree to collect all paths, commands, and arguments
- For each
node:cmd
→ generates bothget
(list) andGET /path
(single)GET /path/{id}
→ generatesadd
with arg-based request bodyPUT /path
→ generatesset
with arg-based request bodyPATCH /path/{id}
→ generatesremoveDELETE /path/{id}- Other commands →
POST /path/command
- For each
under a command, generate request body properties or query parametersarg - The
field from syntax lookups becomes the descriptiondesc
The .proplist and .query Parameters
All POST-based command endpoints accept two special parameters:
— selects which properties to return (like SQL SELECT).proplist
— filter expression array (like SQL WHERE).query
These are RouterOS REST API conventions, not standard REST patterns.
Output Formats
The inspect tree can be converted to multiple schema formats:
inspect.json (Raw Output)
The raw tree as returned by recursive
/console/inspect calls. Each node has:
{ "address": { "_type": "path", "add": { "_type": "cmd", "address": { "_type": "arg", "desc": "IP address" }, "interface": { "_type": "arg", "desc": "Interface name" } }, "set": { "_type": "cmd", ... }, "print": { "_type": "cmd", ... } } }
RAML 1.0 (schema.raml)
Converted to RAML 1.0 resource/method notation:
/ip: /address: get: queryParameters: ... responses: ... put: body: application/json: properties: address: { type: any, description: "IP address" } /{id}: get: ... patch: ... delete: ...
OpenAPI 3.0 (openapi.json)
Standard OpenAPI 3.0 schema generated from the same inspect tree (7.21.1+).
The inspect.json Data Model
Each version's
inspect.json is the canonical source of truth for that RouterOS version's
command tree. It captures:
- Every navigable path in the CLI hierarchy
- Every executable command at each path level
- Every argument (parameter) for each command
- Syntax descriptions for arguments
Tools can parse
inspect.json offline without needing a live router — set INSPECTFILE env var
and the schema generator will use the cached file instead of querying a router.
Common Patterns for Working with the Tree
Finding Commands at a Path
// Given an inspect.json node for /ip/address const node = inspectData.ip.address; // Commands are children with _type === "cmd" const commands = Object.entries(node) .filter(([key, val]) => val._type === "cmd") .map(([key]) => key); // → ["add", "set", "remove", "get", "print", "export", ...]
Finding Arguments for a Command
// Arguments of /ip/address/add const addCmd = inspectData.ip.address.add; const args = Object.entries(addCmd) .filter(([key, val]) => val._type === "arg") .map(([key, val]) => ({ name: key, description: val.desc })); // → [{name: "address", description: "IP address"}, ...]
Traversing Directories
// Directories and paths (navigable children) const children = Object.entries(node) .filter(([key, val]) => val._type === "dir" || val._type === "path") .map(([key]) => key);
Performance Notes
- Full tree traversal takes many minutes against a live router (thousands of HTTP requests,
each a separate POST to
). With KVM acceleration the CHR responds quickly, but the sheer number of sequential requests adds up./console/inspect - Each
call is a separate HTTP request — no batch API/console/inspect - Use
for development/testing to avoid repeated live queriesINSPECTFILE - The tree is version-specific — different RouterOS versions have different command sets
- Extra packages (container, iot, zerotier, etc.) add additional command tree branches
Additional Resources
- For REST API details: see
skill → REST API patternsrouteros-fundamentals - For running a CHR to query: see the
skillrouteros-qemu-chr - For /app YAML format (a feature visible in the tree under 7.22+): see the
skillrouteros-app-yaml