mcpField
R
routeros-command-tree

routeros-command-tree

github.com/tikoci开发8362 次安装5 天前更新
代码仓库

关于 routeros-command-tree

介绍 RouterOS 命令树及其 `/console/inspect` REST 端点的技术 Skill,面向构建 RouterOS 工具、模式或命令查询功能的开发者,并说明遍历、版本差异与谨慎处理风险请求的方法。

主要功能

  1. 01说明命令树节点类型
  2. 02查询 `/console/inspect` 请求
  3. 03遍历并缓存命令树
  4. 04映射 CLI 命令与 REST 动词
  5. 05生成 RAML 和 OpenAPI 模式
  6. 06保守处理风险路径

适用场景

  1. 01构建 RouterOS 命令查询工具
  2. 02生成 REST API 模式
  3. 03离线检查缓存的命令树数据

MCP 相关

该 Skill 说明 `rosetta` 如何使用命令树数据进行 MCP 命令查询,但未说明 MCP 服务的实现方式。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tikoci/routeros-skills --skill routeros-command-tree

安装后在 Claude Code 中输入/routeros-command-tree确认已加载。

详细介绍

RouterOS 命令树与 /console/inspect

概览

RouterOS 将所有配置和命令组织成一棵层次结构树。CLI 中的每个路径 (如 /ip/address/add)都对应树中的一个节点。/console/inspect REST 端点 可让你以编程方式探索整棵树——restraml(RAML/OpenAPI 架构生成器)和 rosetta(MCP 命令查询)等工具就是通过这种方式构建数据库的。

命令树结构

RouterOS 的命令层次结构包含四种节点类型:

节点类型含义示例
dir目录——包含子路径/ip、/system
path路径——可导航的层级(通常包含命令)/ip/address、/interface/bridge
cmd命令——可执行的操作add、set、print、remove、get、export
arg参数——命令的一个参数address=、interface=、disabled=
树示例
/ (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

端点
POST /rest/console/inspect

需要基本身份验证。适用于所有 RouterOS 7.x 版本。

请求类型
请求用途返回内容
child列出某路径的子节点{type: "child", name, "node-type"} 数组
syntax获取节点的帮助文本{type: "syntax", text} 数组
highlight语法高亮数据每字节的令牌类别——参见 routeros-syntax-inspection 技能
completionTab 补全建议补全候选项——有效性检查请参见 routeros-syntax-inspection
列出子节点
// 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" },
//   ...
// ]
获取语法帮助
// 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 字段使用逗号分隔的片段(而非斜杠):

  • 根路径:""(空字符串)
  • /ip:"ip"
  • /ip/address:"ip,address"
  • /ip/address/add:"ip,address,add"
  • /ip/address/add → address 参数:"ip,address,add,address"

使用 JavaScript 的 Array.toString() 方法时,数组会自然生成这种逗号分隔格式:["ip", "address", "add"].toString() → "ip,address,add"。

树遍历模式

递归遍历整棵树:

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;
}
危险路径——保守跳过策略

在旧版 RouterOS 上,通过 /console/inspect 查询脚本关键字路径的语法或补全信息时,可能会导致 REST 服务死锁。爬虫会保守地跳过包含以下任一名称的子树中的语法查询:

where, do, else, rule, command, on-error

这些是 RouterOS 脚本结构。应将这六个路径视为保守的爬虫跳过策略,而非永恒不变的崩溃规律:在 tikoci/restraml 中进行的实时探测(MikroTik 支持案例 SUP-127641) 确认,只有对 RouterOS ≤7.20.8 上的裸 do 使用 request=syntax/completion 才会导致死锁——其余五个路径在该版本上都会立即返回 []——并发现此问题已在 7.21.4 中修复。(最初关于这六个路径的说法源自顺序探测:一旦 do 卡住服务,之后测试的每个路径看起来都会导致崩溃。)在所有已测试版本上,枚举子节点(child 请求)都是安全的,["do","command"] 这类嵌套路径也同样安全。

保守做法(示例中采用的做法)是在任一祖先路径匹配危险路径时跳过整个参数,并在旧版或未知版本上为每个请求设置较短的超时时间。实际的 rest2raml.js 实现采用了这种模式。

const DANGEROUS_PATHS = ["where", "do", "else", "rule", "command", "on-error"];

CLI 命令 → REST 动词映射

RouterOS CLI 命令在 REST API 中映射为 HTTP 动词:

CLI 命令HTTP 动词REST URL 模式说明
get(打印)GET/rest/ip/address返回所有条目的数组
get(单个)GET/rest/ip/address/*1根据 ID 返回单个条目
addPUT/rest/ip/address创建新条目(不是 POST!)
setPATCH/rest/ip/address/*1更新现有条目
removeDELETE/rest/ip/address/*1根据 ID 删除条目
printPOST/rest/ip/address/print操作风格(也可通过 GET 使用)
其他命令POST/rest/path/command操作,例如重启、清空等

关键要点: REST 中的 PUT = 创建,PATCH = 更新。这与许多 REST API 约定相反,后者通常使用 PUT 进行幂等更新,使用 POST 创建。

RAML/OpenAPI 架构生成

根据命令树生成 API 架构时:

  1. 遍历命令树,收集所有路径、命令和参数
  2. 对于每个 cmd 节点:
    • get → 生成 GET /path(列表)和 GET /path/{id}(单个)
    • add → 生成 PUT /path,请求体根据参数生成
    • set → 生成 PATCH /path/{id},请求体根据参数生成
    • remove → 生成 DELETE /path/{id}
    • 其他命令 → POST /path/command
  3. 对于命令下的每个 arg,生成请求体属性或查询参数
  4. 语法查询中的 desc 字段会成为描述
.proplist 和 .query 参数

所有基于 POST 的命令端点都接受两个特殊参数:

  • .proplist — 选择要返回的属性(类似 SQL SELECT)
  • .query — 筛选表达式数组(类似 SQL WHERE)

这些是 RouterOS REST API 的约定,并非标准 REST 模式。

输出格式

inspect 树可转换为多种架构格式:

inspect.json(原始输出)

递归调用 /console/inspect 返回的原始树。每个节点包含:

{
  "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)

转换为 RAML 1.0 资源/方法表示法:

/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)

根据同一 inspect 树生成的标准 OpenAPI 3.0 架构(7.21.1+)。

inspect.json 数据模型

每个版本的 inspect.json 都是该 RouterOS 版本命令树的权威事实来源。它包含:

  • CLI 层次结构中的所有可导航路径
  • 每个路径层级中所有可执行的命令
  • 每个命令的所有参数
  • 参数的语法描述

工具可以离线解析 inspect.json,无需连接到运行中的路由器 — 设置 INSPECTFILE 环境变量后,架构生成器会使用缓存文件,而不是查询路由器。

使用命令树的常见模式

查找路径中的命令
// 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", ...]
查找命令的参数
// 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"}, ...]
遍历目录
// Directories and paths (navigable children)
const children = Object.entries(node)
  .filter(([key, val]) => val._type === "dir" || val._type === "path")
  .map(([key]) => key);

性能说明

  • 在运行中的路由器上遍历完整命令树需要数分钟(数千个 HTTP 请求,每个请求都是对 /console/inspect 的单独 POST)。借助 KVM 加速,CHR 的响应很快,但大量顺序请求累积起来仍需不少时间。
  • 每次 /console/inspect 调用都是单独的 HTTP 请求 — 没有批处理 API
  • 开发/测试时使用 INSPECTFILE,避免重复进行实时查询
  • 命令树与版本相关 — 不同 RouterOS 版本的命令集不同
  • 额外的软件包(container、iot、zerotier 等)会增加其他命令树分支

附加资源

  • MikroTik 的新版手册网站提供可浏览的 CLI 参考,网址为 https://manual.mikrotik.com/docs/CLI%20Reference/。它按软件包/路径展示命令菜单、 标志、参数名称和类型(例如, /docs/CLI%20Reference/system/ip/address)。可将其视为 /console/inspect 的 人类可读版配套资料;如果它与运行中的路由器不一致,应以该路由器上的 /console/inspect 为准。
  • 有关 REST API 的详情:请参阅 routeros-fundamentals 技能 → REST API 模式
  • 有关运行 CHR 以进行查询:请参阅 routeros-qemu-chr 技能
  • 有关 /app YAML 格式(这是 7.22+ 的树形结构中可见的一项功能):请参阅 routeros-app-yaml 技能

评论

支持 Markdown