mcpField
R
routeros-command-tree

routeros-command-tree

github.com/tikoci開発8362 回インストール5 日前に更新
リポジトリ

routeros-command-tree について

RouterOS のコマンドツリーと `/console/inspect` REST エンドポイントを解説する技術スキルです。RouterOS 用ツールやスキーマ、コマンド検索機能を開発する人向けに、ツリー走査、バージョン差異、リスクのある構文要求への対処を説明します。

主な機能

  1. 01コマンドツリーのノード型を解説
  2. 02`/console/inspect` の要求を実行
  3. 03コマンドツリーの走査とキャッシュ
  4. 04CLI コマンドと REST 動詞の対応付け
  5. 05RAML と OpenAPI スキーマの生成
  6. 06危険なパスを保守的に処理

活用シーン

  1. 01RouterOS コマンド検索ツールの構築
  2. 02REST API スキーマの生成
  3. 03キャッシュ済みコマンドツリーのオフライン確認

MCP 対応

`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 のコマンド階層には、次の 4 種類のノードがあります。

ノードの種類意味例
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 スキルを参照してください
completionタブ補完の候補補完候補 — 有効性の確認については 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 arg:"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 のスクリプト構文要素です。この 6 パスのリストは、時代を問わずクラッシュが発生することを示すものではなく、保守的なクローラーのスキップポリシーとして扱ってください。 tikoci/restraml での実機調査(MikroTik サポートケース SUP-127641)では、 デッドロックが発生すると確認されたのは RouterOS ≤7.20.8 で、request=syntax/completion を指定して do 単体を照会した場合だけであり、ほかの 5 つのパスはその環境で瞬時に [] を返すことが判明しました。また、この問題は7.21.4 で修正されています。(元になった 6 パスの通説は、順番に照会した際に生まれたものです。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 (print)GET/rest/ip/addressすべてのエントリーを配列で返します
get (single)GET/rest/ip/address/*1ID で指定した単一のエントリー
addPUT/rest/ip/address新しいエントリーを作成します(POST ではありません!)
setPATCH/rest/ip/address/*1既存のエントリーを更新します
removeDELETE/rest/ip/address/*1ID でエントリーを削除します
printPOST/rest/ip/address/printアクション形式(GET としても動作します)
その他のコマンドPOST/rest/path/commandアクション(再起動、フラッシュなど)

重要なポイント: REST の PUT は作成、PATCH は更新です。これは、PUT を冪等な更新、POST を作成に使う多くの REST API 規約とは逆です。

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 ベースのコマンドエンドポイントは、すべて次の 2 つの特殊なパラメーターを受け付けます。

  • .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 スキルを参照してください

コメント