Tool 擴展方案設計
Arova Nexus — Phase 0 Product Definition | 2026-03-31

版本: v1.0 | 日期: 2026-03-26
問題: 使用者需要的資料超出現有 LibreNMS/Graylog Tools 時,如何處理?


方案總覽

# 方案 開發成本 使用者門檻 彈性 安全性 推薦場景
1 Generic HTTP Tool 任何有 REST API 的系統
2 SSH Command Tool On-prem 伺服器指令
3 MCP Server 動態掛載 最高 長期擴展、生態整合
4 Agent 自寫程式碼 最低 一次性分析、快速驗證
5 Low-Code Connector Builder 最低 非工程師自助建立
6 Plugin 目錄熱載入 開發團隊標準化擴展

方案 1:Generic HTTP Tool(最快上手)

內建一個萬用 HTTP 呼叫 Tool,Agent 可以打任何 REST API。

// src/tools/generic-http.ts
export const http_request = tool(
  async ({ method, url, headers, body, jsonPath }) => {
    const res = await fetch(url, {
      method,
      headers: { "Content-Type": "application/json", ...headers },
      body: body ? JSON.stringify(body) : undefined,
    });
    const data = await res.json();

    // 可選:用 jsonPath 提取特定欄位
    if (jsonPath) {
      return JSON.stringify(extractJsonPath(data, jsonPath));
    }
    return JSON.stringify(data);
  },
  {
    name: "http_request",
    description: "呼叫任意 REST API。用於現有 Tool 不支援的資料源。",
    schema: z.object({
      method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET"),
      url: z.string().describe("完整 API URL"),
      headers: z.record(z.string()).optional().describe("HTTP headers(如認證)"),
      body: z.any().optional().describe("Request body(POST/PUT 用)"),
      jsonPath: z.string().optional().describe("從回應中提取的 JSON path,如 'data.items'"),
    }),
  }
);

使用情境:

使用者:「查一下 vCenter 有哪些 VM」

Agent:目前沒有 vCenter 專用 Tool,但我可以直接呼叫 vCenter REST API。
       請提供:
       1. vCenter IP 和 port
       2. 認證方式(帳號密碼或 Token)

使用者:「IP 是 10.20.92.50,帳號 admin,密碼 xxx」

Agent:[http_request({
         method: "POST",
         url: "https://10.20.92.50/api/session",
         headers: { "Content-Type": "application/json" },
         body: { username: "admin", password: "xxx" }
       })]
       → 取得 session token

       [http_request({
         method: "GET",
         url: "https://10.20.92.50/api/vcenter/vm",
         headers: { "vmware-api-session-id": "<token>" },
         jsonPath: "value"
       })]
       → 取得 VM 清單

       找到 15 台 VM:
       | VM 名稱 | 狀態 | CPU | 記憶體 |
       |---------|------|-----|--------|
       | ...     | ...  | ... | ...    |

優點: 零開發,Agent 自己組裝 API 呼叫
缺點: 每次都要 Agent 重新推理 API 格式;認證資訊在對話中傳遞(安全風險)
改進: 搭配 Secrets Manager 存放認證資訊


方案 2:SSH Command Tool(On-Prem 萬能)

內建一個可以 SSH 到指定主機執行命令的 Tool。適合沒有 API 的系統。

// src/tools/ssh-command.ts
import { NodeSSH } from "node-ssh";

export const ssh_command = tool(
  async ({ host, command, username, keyPath }) => {
    const ssh = new NodeSSH();
    await ssh.connect({
      host,
      username: username || process.env.SSH_DEFAULT_USER,
      privateKeyPath: keyPath || process.env.SSH_KEY_PATH,
    });
    const result = await ssh.execCommand(command);
    ssh.dispose();

    return JSON.stringify({
      stdout: result.stdout,
      stderr: result.stderr,
      exitCode: result.code,
    });
  },
  {
    name: "ssh_command",
    description: "SSH 到指定主機執行命令。用於沒有 API 的系統(需要 SSH Key 預先設定)。",
    schema: z.object({
      host: z.string().describe("目標主機 IP"),
      command: z.string().describe("要執行的 shell 指令"),
      username: z.string().optional(),
      keyPath: z.string().optional(),
    }),
  }
);

使用情境:

使用者:「查一下備份系統昨天有沒有成功」

Agent:目前沒有備份系統的 Tool,但我可以 SSH 查看。
       備份伺服器的 IP 是?

使用者:「10.20.92.100」

Agent:[ssh_command({
         host: "10.20.92.100",
         command: "tail -20 /var/log/backup/backup.log | grep -E 'SUCCESS|FAIL'"
       })]

       昨日備份結果:
       ✅ 2026-03-25 02:00 — Full Backup — SUCCESS (耗時 2h 15m)
       ✅ 2026-03-25 14:00 — Incremental — SUCCESS (耗時 12m)

優點: 幾乎任何 Linux 系統都能查
缺點: 安全風險最高(Agent 可執行任意指令);需要 SSH Key 預設定
必要限制:

// 安全限制:白名單 + 只讀指令
const ALLOWED_HOSTS = ["10.20.92.100", "10.20.92.200", "10.20.92.201"];
const BLOCKED_COMMANDS = /rm\s|dd\s|mkfs|shutdown|reboot|passwd|chmod\s777/;

if (!ALLOWED_HOSTS.includes(host)) throw new Error("Host not in whitelist");
if (BLOCKED_COMMANDS.test(command)) throw new Error("Destructive command blocked");

方案 3:MCP Server 動態掛載(最推薦的長期方案)

使用 Anthropic 的 Model Context Protocol (MCP),每個資料源是一個獨立的 MCP Server,Agent 動態掛載和呼叫。

什麼是 MCP

Agent (MCP Client)
    ├── MCP Server: LibreNMS     → devices, health, alerts, ports
    ├── MCP Server: Graylog      → search, stats, events
    ├── MCP Server: vCenter      → vms, hosts, datastores     ← 新增!
    ├── MCP Server: Backup       → jobs, status, schedules    ← 新增!
    └── MCP Server: Fortinet     → vpn, policies, sessions    ← 新增!

MCP Server 範例(vCenter)

// mcp-servers/vcenter/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({ name: "vcenter", version: "1.0.0" });

server.tool("list_vms", { description: "列出 vCenter 所有 VM" },
  z.object({ datacenter: z.string().optional() }),
  async ({ datacenter }) => {
    const session = await vcenterLogin();
    const vms = await vcenterApi("/api/vcenter/vm", session);
    return { content: [{ type: "text", text: JSON.stringify(vms) }] };
  }
);

server.tool("get_vm_metrics", { description: "查詢 VM 的 CPU/Memory 使用率" },
  z.object({ vmId: z.string() }),
  async ({ vmId }) => {
    const metrics = await vcenterApi(`/api/vcenter/vm/${vmId}`, session);
    return { content: [{ type: "text", text: JSON.stringify(metrics) }] };
  }
);

// 啟動 MCP Server(stdio 或 SSE transport)
server.listen({ transport: "stdio" });

Agent 端掛載

// src/mcp/loader.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// 從設定檔動態載入 MCP Servers
const mcpConfig = yaml.parse(readFileSync("./mcp-config.yaml", "utf-8"));

for (const server of mcpConfig.servers) {
  const transport = new StdioClientTransport({
    command: server.command,  // e.g., "node"
    args: server.args,        // e.g., ["./mcp-servers/vcenter/index.js"]
    env: server.env,          // e.g., { VCENTER_URL: "...", VCENTER_TOKEN: "..." }
  });

  const client = new Client({ name: `mcp-${server.name}` });
  await client.connect(transport);

  // 取得該 MCP Server 提供的所有 Tools
  const tools = await client.listTools();
  // 註冊到 Agent 的 Tool Registry
  tools.forEach(t => toolRegistry.register(t));
}
# mcp-config.yaml — 動態掛載設定
servers:
  - name: librenms
    command: node
    args: ["./mcp-servers/librenms/index.js"]
    env:
      LIBRENMS_URL: "http://100.98.129.83/api/v0"
      LIBRENMS_TOKEN: "${LIBRENMS_TOKEN}"

  - name: graylog
    command: node
    args: ["./mcp-servers/graylog/index.js"]
    env:
      GRAYLOG_URL: "http://100.98.20.188:9000/api"
      GRAYLOG_TOKEN: "${GRAYLOG_TOKEN}"

  # 新增資料源只需要加一段設定 + 放入 MCP Server
  - name: vcenter
    command: node
    args: ["./mcp-servers/vcenter/index.js"]
    env:
      VCENTER_URL: "https://10.20.92.50"
      VCENTER_USER: "${VCENTER_USER}"
      VCENTER_PASS: "${VCENTER_PASS}"

新增資料源的流程

開發者建立 MCP Server(一個 .ts 檔案)
    ↓
在 mcp-config.yaml 加一段設定
    ↓
重啟 Agent(或支援熱載入)
    ↓
Supervisor 自動發現新 Tools
    ↓
使用者透過 Chat 直接使用

優點:

缺點: 比直接寫 Tool function 多一層抽象


方案 4:Agent 自寫程式碼(Code Interpreter)

讓 Agent 擁有執行 JavaScript/TypeScript 的能力,遇到不支援的資料源時自己寫 code 解決。

// src/tools/code-executor.ts
import { VM } from "vm2";

export const execute_code = tool(
  async ({ code, description }) => {
    const vm = new VM({
      timeout: 10000,  // 10 秒超時
      sandbox: {
        fetch,         // 允許 HTTP 呼叫
        console: { log: (...args) => logs.push(args.join(" ")) },
        JSON,
        Buffer,
      },
    });

    const result = vm.run(code);
    return JSON.stringify({ result, logs });
  },
  {
    name: "execute_code",
    description: "執行 JavaScript 程式碼。用於一次性資料處理或快速驗證。沙箱環境,有 fetch 可呼叫 API。",
    schema: z.object({
      code: z.string().describe("要執行的 JavaScript 程式碼"),
      description: z.string().describe("這段程式碼在做什麼(供稽核用)"),
    }),
  }
);

使用情境:

使用者:「幫我算一下每台設備的磁碟剩餘天數,
         用過去 7 天的增量推算」

Agent:現有 Tool 沒有趨勢預測功能,我寫一段程式來算。

       [execute_code({
         code: `
           // 取得 7 天前和今天的磁碟使用率
           const today = await fetch('...librenms.../storage').then(r => r.json());
           const weekAgo = await fetch('...').then(r => r.json());

           // 計算每日增量和預估滿載天數
           const predictions = today.map((disk, i) => {
             const dailyGrowth = (disk.used - weekAgo[i].used) / 7;
             const remaining = disk.free / dailyGrowth;
             return {
               device: disk.hostname,
               disk: disk.descr,
               currentPercent: disk.percent,
               dailyGrowthGB: (dailyGrowth / 1e9).toFixed(2),
               daysUntilFull: Math.round(remaining),
             };
           });

           return predictions.sort((a, b) => a.daysUntilFull - b.daysUntilFull);
         `,
         description: "計算磁碟剩餘天數預測"
       })]

       預測結果:
       | 設備 | 磁碟 | 目前 | 日增 | 預估滿載 |
       |------|------|------|------|---------|
       | mes-db-2 | E: | 78% | 0.5 GB/天 | 約 90 天 |
       | th-ex001 | D: | 72% | 0.2 GB/天 | 約 200 天 |

優點: 零限制,Agent 可以做任何運算
缺點: 安全風險(需要嚴格沙箱);執行結果不可預測
必要限制: vm2 沙箱 + 網路白名單 + 執行超時 + 稽核日誌


方案 5:Low-Code Connector Builder(非工程師友善)

在 Chat UI 中提供引導式的連接器建立流程。

使用者:「我要加入一個新的資料源」

Agent:「好的,請告訴我:

        1. 資料源名稱?
        2. 連線方式?
           ○ REST API(最常見)
           ○ SSH
           ○ 資料庫(MySQL/PostgreSQL)
           ○ 檔案(CSV/JSON)」

使用者:「Fortinet FortiGate 防火牆,REST API」

Agent:「請提供:
        - API URL:
        - 認證方式:(API Key / 帳號密碼 / OAuth)
        - 想查詢的資料:(VPN 連線 / Policy / Session / ...)」

使用者:「https://10.20.92.1/api/v2, API Key = xxx, 想看 VPN 連線」

Agent:讓我測試連線...
       [http_request → 測試 /api/v2/monitor/vpn/ipsec]
       ✅ 連線成功!找到 3 條 IPSec 通道。

       我已建立 Connector 設定:
       ┌──────────────────────────────────┐
       │ 名稱:FortiGate VPN Monitor      │
       │ URL:https://10.20.92.1/api/v2  │
       │ 認證:API Key(已加密儲存)       │
       │ 可查詢:                          │
       │  • /monitor/vpn/ipsec — IPSec 通道│
       │  • /monitor/vpn/ssl — SSL VPN    │
       │  • /monitor/system/status — 狀態  │
       └──────────────────────────────────┘

       要儲存嗎?儲存後我就能在報表中使用這個資料源。

使用者:「存」

Agent:[save → connectors/fortigate.yaml]
       ✅ 已儲存。以後你可以說「查 FortiGate 的 VPN 連線」,
       我就會自動使用這個 Connector。
# connectors/fortigate.yaml — 自動產生
name: fortigate
display_name: FortiGate 防火牆
type: rest_api
base_url: https://10.20.92.1/api/v2
auth:
  type: api_key
  header: Authorization
  value_ref: secrets/fortigate_api_key  # 加密存儲
endpoints:
  - name: vpn_ipsec
    path: /monitor/vpn/ipsec
    method: GET
    description: "IPSec VPN 通道狀態"
  - name: vpn_ssl
    path: /monitor/vpn/ssl
    method: GET
    description: "SSL VPN 連線"
  - name: system_status
    path: /monitor/system/status
    method: GET
    description: "系統狀態"
created_by: kyle
created_at: 2026-03-26

方案 6:Plugin 目錄熱載入

開發團隊標準化的擴展方式。放入一個目錄就自動生效。

plugins/
├── librenms/          # 已內建
│   ├── plugin.yaml    # 元資料 + Tool 清單
│   ├── tools.ts       # Tool 實作
│   └── prompts.ts     # Agent Prompt
├── graylog/           # 已內建
│   └── ...
├── vcenter/           # 新增!放入即生效
│   ├── plugin.yaml
│   ├── tools.ts
│   └── prompts.ts
└── fortigate/         # 新增!
    └── ...
# plugins/vcenter/plugin.yaml
name: vcenter
version: 1.0.0
description: "VMware vCenter 整合"
author: arova-team
model:
  default: ollama/qwen3.5:35b-a3b
tools:
  - name: list_vms
    description: "列出 vCenter 所有 VM"
  - name: get_vm_metrics
    description: "查詢 VM CPU/Memory"
env_required:
  - VCENTER_URL
  - VCENTER_USER
  - VCENTER_PASS

推薦的組合策略

不是只選一個,而是分層疊加

┌─────────────────────────────────────────────────┐
│ Layer 4: Plugin 目錄(開發團隊正式交付)          │
│          最高品質、有測試、有文件                  │
├─────────────────────────────────────────────────┤
│ Layer 3: MCP Server(標準化擴展)                │
│          獨立 process、隔離性好、可跨語言         │
├─────────────────────────────────────────────────┤
│ Layer 2: Low-Code Connector(使用者自助建立)     │
│          Agent 引導、YAML 設定、自動測試          │
├─────────────────────────────────────────────────┤
│ Layer 1: Generic HTTP + SSH(萬能兜底)          │
│          零設定、Agent 自己組裝 API 呼叫          │
├─────────────────────────────────────────────────┤
│ Layer 0: Code Interpreter(終極兜底)            │
│          Agent 自己寫程式碼解決                   │
└─────────────────────────────────────────────────┘

開發路線

Phase 實作 工期
Phase 1(MVP) Generic HTTP Tool + SSH Tool 1 天
Phase 2 Low-Code Connector Builder(對話式建立 + YAML 儲存) 3 天
Phase 3 MCP Server 架構(LibreNMS/Graylog 改為 MCP Server) 1 週
Phase 4 Plugin 目錄熱載入 + Marketplace 概念 2 週
Phase 5 Code Interpreter(沙箱化) 3 天

決策樹

使用者需要新資料 →
    │
    ├── 只是一次性查詢?
    │   └── YES → Generic HTTP Tool(Agent 直接打 API)
    │
    ├── 會重複使用?
    │   └── YES → Low-Code Connector(引導建立 + 儲存 YAML)
    │
    ├── 需要複雜邏輯或多步驟?
    │   └── YES → MCP Server 或 Plugin(開發團隊處理)
    │
    └── 目標系統沒有 API?
        └── YES → SSH Tool(直接查 log/指令輸出)