@usex/mikrotik-mcp 3.17.0 → 3.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +30 -10
- package/dist/index.js +26 -9
- package/dist/ui/observability.html +2 -2
- package/package.json +1 -1
- package/schemas/tool-catalog.json +19 -4
- package/schemas/tools/create_local_backup.json +16 -1
package/dist/cli.js
CHANGED
|
@@ -2174,7 +2174,7 @@ ${result}`;
|
|
|
2174
2174
|
name: "create_export",
|
|
2175
2175
|
title: "Create Full Configuration Export",
|
|
2176
2176
|
annotations: READ,
|
|
2177
|
-
description: "Exports the complete device configuration to a `.rsc` plain-text script file (`/export" + " file=<name>`), re-applicable via `import_configuration`. Unlike `create_backup`, the result" + " is human-readable plain text \u2014 not a binary snapshot \u2014 and passwords are hidden by default" + " (`hide_sensitive=true`). `file_format` changes only the file extension used when looking up" + " the saved file \u2014 no `format=` flag is ever sent to RouterOS, so the content is always" + " RouterOS script text regardless of the chosen extension; selecting `json` or `xml` will also" + " cause the post-export file lookup to fail because RouterOS saves the file as `.rsc`.
|
|
2177
|
+
description: "Exports the complete device configuration to a `.rsc` plain-text script file (`/export" + " file=<name>`), re-applicable via `import_configuration`. Unlike `create_backup`, the result" + " is human-readable plain text \u2014 not a binary snapshot \u2014 and passwords are hidden by default" + " (`hide_sensitive=true`). `file_format` changes only the file extension used when looking up" + " the saved file \u2014 no `format=` flag is ever sent to RouterOS, so the content is always" + " RouterOS script text regardless of the chosen extension; selecting `json` or `xml` will also" + " cause the post-export file lookup to fail because RouterOS saves the file as `.rsc`." + " ALWAYS default to a FULL configuration export: leave `compact` and `verbose` off (the plain" + " full export) unless the user explicitly asks for less or more. Set `compact` ONLY when the" + " user wants a smaller diff that omits default values, or `verbose` ONLY when they want every" + " parameter (including defaults). `export_type` only controls where the `file=` argument is" + " positioned in the command and does not independently drive compactness or verbosity. For a" + " single subsection only use `export_section`. Returns file details of the created export file.",
|
|
2178
2178
|
inputSchema: {
|
|
2179
2179
|
name: z4.string().optional(),
|
|
2180
2180
|
file_format: z4.enum(["rsc", "json", "xml"]).default("rsc"),
|
|
@@ -2367,6 +2367,12 @@ ${result}`;
|
|
|
2367
2367
|
// src/tools/local-backup.ts
|
|
2368
2368
|
import { z as z5 } from "zod";
|
|
2369
2369
|
|
|
2370
|
+
// src/core/slug.ts
|
|
2371
|
+
function deviceSlug(name) {
|
|
2372
|
+
const s = (name ?? "").replace(/[^A-Za-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2373
|
+
return s || "device";
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2370
2376
|
// src/backups/vault.ts
|
|
2371
2377
|
import {
|
|
2372
2378
|
existsSync,
|
|
@@ -2487,15 +2493,17 @@ function labelSlug(label) {
|
|
|
2487
2493
|
const s = (label ?? "").replace(/[^A-Za-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
2488
2494
|
return s ? `_${s}` : "";
|
|
2489
2495
|
}
|
|
2496
|
+
function buildExportCommand(opts = {}) {
|
|
2497
|
+
return new Cmd("/export").raw(opts.verbose ? "verbose" : opts.compact ? "compact" : undefined).raw(opts.terse ? "terse" : undefined).raw(opts.showSensitive ? "show-sensitive" : undefined).build();
|
|
2498
|
+
}
|
|
2490
2499
|
async function createLocalBackup(ctx, opts = {}) {
|
|
2491
2500
|
const device = resolveDeviceName(ctx.device);
|
|
2492
|
-
const
|
|
2493
|
-
const body = await executeMikrotikCommand(cmd, ctx);
|
|
2501
|
+
const body = await executeMikrotikCommand(buildExportCommand(opts), ctx);
|
|
2494
2502
|
if (isEmpty(body) || looksLikeError(body)) {
|
|
2495
2503
|
return { ok: false, device, error: body.trim() || "(empty export)" };
|
|
2496
2504
|
}
|
|
2497
2505
|
const stamp = await deviceDateStamp(ctx);
|
|
2498
|
-
const name = `${device}_${stamp}${labelSlug(opts.label)}.rsc`;
|
|
2506
|
+
const name = `${deviceSlug(device)}_${stamp}${labelSlug(opts.label)}.rsc`;
|
|
2499
2507
|
return { ok: true, device, name: writeBackup(name, body), bytes: Buffer.byteLength(body) };
|
|
2500
2508
|
}
|
|
2501
2509
|
|
|
@@ -2566,13 +2574,22 @@ var localBackupTools = [
|
|
|
2566
2574
|
name: "create_local_backup",
|
|
2567
2575
|
title: "Create Local Config Backup (host vault)",
|
|
2568
2576
|
annotations: WRITE,
|
|
2569
|
-
description: "Captures the device's full configuration with `/export` and saves it as a timestamped " + "plain-text `.rsc` file in the MCP server's LOCAL backup vault (default " + "`~/.mikrotik-mcp/backups/`, override with the `MIKROTIK_BACKUP_DIR` env var) \u2014 NOT on the " + "device and NOT in S3. The filename is `<device>_<date_time>.rsc` stamped in the device's
|
|
2577
|
+
description: "Captures the device's full configuration with `/export` and saves it as a timestamped " + "plain-text `.rsc` file in the MCP server's LOCAL backup vault (default " + "`~/.mikrotik-mcp/backups/`, override with the `MIKROTIK_BACKUP_DIR` env var) \u2014 NOT on the " + "device and NOT in S3. The filename is `<device-slug>_<date_time>.rsc` \u2014 the device name is " + "slugified (spaces/underscores/etc \u2192 dash) \u2014 stamped in the device's local clock (24-hour; " + "Jalali for the Tehran timezone, Gregorian otherwise). This is a host-side, human-readable, " + "diffable copy you can restore later with restore_local_backup. Compare: create_backup makes " + "a binary `/system backup` file ON the device; capture_config_snapshot stores an export in " + "the local snapshot database; upload_backup_to_s3 pushes to S3. ALWAYS default to a FULL " + "backup of the complete configuration: call this with no option flags (the bare `/export`) " + "unless the user explicitly asks for less or more. Narrow it ONLY on request \u2014 set compact to " + "drop default values, or use export_section for a single subsection. Broaden it ONLY on " + "request \u2014 set verbose to include every parameter (even defaults), show_sensitive to include " + "secrets (keys/passwords), or use create_backup for a binary full-system snapshot. terse just " + "changes the text to one machine-readable line per item. Returns the saved filename, byte size " + "and vault path.",
|
|
2570
2578
|
inputSchema: {
|
|
2571
2579
|
label: z5.string().optional().describe('Optional label appended to the filename, e.g. "pre-upgrade".'),
|
|
2572
|
-
show_sensitive: z5.boolean().default(false).describe("Include secrets (keys/passwords) in the export. Default false.")
|
|
2580
|
+
show_sensitive: z5.boolean().default(false).describe("Include secrets (keys/passwords) in the export. Default false."),
|
|
2581
|
+
verbose: z5.boolean().default(false).describe("Include every parameter, even defaults (RouterOS `verbose`)."),
|
|
2582
|
+
compact: z5.boolean().default(false).describe("Export only non-default values (RouterOS `compact`; ignored if verbose)."),
|
|
2583
|
+
terse: z5.boolean().default(false).describe("One self-contained, machine-readable line per item (RouterOS `terse`).")
|
|
2573
2584
|
},
|
|
2574
2585
|
async handler(a, ctx) {
|
|
2575
|
-
const r = await createLocalBackup(ctx, {
|
|
2586
|
+
const r = await createLocalBackup(ctx, {
|
|
2587
|
+
label: a.label,
|
|
2588
|
+
showSensitive: a.show_sensitive,
|
|
2589
|
+
verbose: a.verbose,
|
|
2590
|
+
compact: a.compact,
|
|
2591
|
+
terse: a.terse
|
|
2592
|
+
});
|
|
2576
2593
|
if (!r.ok)
|
|
2577
2594
|
return `Failed to capture export for a local backup: ${r.error}`;
|
|
2578
2595
|
return `Saved local backup '${r.name}' (${r.bytes} bytes) for device '${r.device}' to ` + `${backupDir()}. Restore it with restore_local_backup name=${r.name}.`;
|
|
@@ -2708,7 +2725,7 @@ function trimSlashes(s) {
|
|
|
2708
2725
|
return s.replace(/^\/+|\/+$/g, "");
|
|
2709
2726
|
}
|
|
2710
2727
|
function s3DevicePrefix(device) {
|
|
2711
|
-
const segments = [getS3Config()?.prefix ?? "", device
|
|
2728
|
+
const segments = [getS3Config()?.prefix ?? "", device ? deviceSlug(device) : ""].map(trimSlashes).filter(Boolean);
|
|
2712
2729
|
return segments.length ? `${segments.join("/")}/` : "";
|
|
2713
2730
|
}
|
|
2714
2731
|
function s3Key(name, device) {
|
|
@@ -23449,7 +23466,10 @@ async function featureRoutes(req, url) {
|
|
|
23449
23466
|
const ctx = createContext(undefined, b?.device);
|
|
23450
23467
|
const r = await createLocalBackup(ctx, {
|
|
23451
23468
|
label: b?.label,
|
|
23452
|
-
showSensitive: b?.show_sensitive === true
|
|
23469
|
+
showSensitive: b?.show_sensitive === true,
|
|
23470
|
+
verbose: b?.verbose === true,
|
|
23471
|
+
compact: b?.compact === true,
|
|
23472
|
+
terse: b?.terse === true
|
|
23453
23473
|
});
|
|
23454
23474
|
return r.ok ? json(r) : json({ error: r.error ?? "export failed" }, 502);
|
|
23455
23475
|
}
|
|
@@ -23768,7 +23788,7 @@ function registerPrompts(server) {
|
|
|
23768
23788
|
// package.json
|
|
23769
23789
|
var package_default = {
|
|
23770
23790
|
name: "@usex/mikrotik-mcp",
|
|
23771
|
-
version: "3.
|
|
23791
|
+
version: "3.19.0",
|
|
23772
23792
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
23773
23793
|
keywords: [
|
|
23774
23794
|
"ai",
|
package/dist/index.js
CHANGED
|
@@ -2186,7 +2186,7 @@ ${result}`;
|
|
|
2186
2186
|
name: "create_export",
|
|
2187
2187
|
title: "Create Full Configuration Export",
|
|
2188
2188
|
annotations: READ,
|
|
2189
|
-
description: "Exports the complete device configuration to a `.rsc` plain-text script file (`/export" + " file=<name>`), re-applicable via `import_configuration`. Unlike `create_backup`, the result" + " is human-readable plain text \u2014 not a binary snapshot \u2014 and passwords are hidden by default" + " (`hide_sensitive=true`). `file_format` changes only the file extension used when looking up" + " the saved file \u2014 no `format=` flag is ever sent to RouterOS, so the content is always" + " RouterOS script text regardless of the chosen extension; selecting `json` or `xml` will also" + " cause the post-export file lookup to fail because RouterOS saves the file as `.rsc`.
|
|
2189
|
+
description: "Exports the complete device configuration to a `.rsc` plain-text script file (`/export" + " file=<name>`), re-applicable via `import_configuration`. Unlike `create_backup`, the result" + " is human-readable plain text \u2014 not a binary snapshot \u2014 and passwords are hidden by default" + " (`hide_sensitive=true`). `file_format` changes only the file extension used when looking up" + " the saved file \u2014 no `format=` flag is ever sent to RouterOS, so the content is always" + " RouterOS script text regardless of the chosen extension; selecting `json` or `xml` will also" + " cause the post-export file lookup to fail because RouterOS saves the file as `.rsc`." + " ALWAYS default to a FULL configuration export: leave `compact` and `verbose` off (the plain" + " full export) unless the user explicitly asks for less or more. Set `compact` ONLY when the" + " user wants a smaller diff that omits default values, or `verbose` ONLY when they want every" + " parameter (including defaults). `export_type` only controls where the `file=` argument is" + " positioned in the command and does not independently drive compactness or verbosity. For a" + " single subsection only use `export_section`. Returns file details of the created export file.",
|
|
2190
2190
|
inputSchema: {
|
|
2191
2191
|
name: z5.string().optional(),
|
|
2192
2192
|
file_format: z5.enum(["rsc", "json", "xml"]).default("rsc"),
|
|
@@ -2379,6 +2379,12 @@ ${result}`;
|
|
|
2379
2379
|
// src/tools/local-backup.ts
|
|
2380
2380
|
import { z as z6 } from "zod";
|
|
2381
2381
|
|
|
2382
|
+
// src/core/slug.ts
|
|
2383
|
+
function deviceSlug(name) {
|
|
2384
|
+
const s = (name ?? "").replace(/[^A-Za-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2385
|
+
return s || "device";
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2382
2388
|
// src/backups/vault.ts
|
|
2383
2389
|
import {
|
|
2384
2390
|
existsSync,
|
|
@@ -2499,15 +2505,17 @@ function labelSlug(label) {
|
|
|
2499
2505
|
const s = (label ?? "").replace(/[^A-Za-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
2500
2506
|
return s ? `_${s}` : "";
|
|
2501
2507
|
}
|
|
2508
|
+
function buildExportCommand(opts = {}) {
|
|
2509
|
+
return new Cmd("/export").raw(opts.verbose ? "verbose" : opts.compact ? "compact" : undefined).raw(opts.terse ? "terse" : undefined).raw(opts.showSensitive ? "show-sensitive" : undefined).build();
|
|
2510
|
+
}
|
|
2502
2511
|
async function createLocalBackup(ctx, opts = {}) {
|
|
2503
2512
|
const device = resolveDeviceName(ctx.device);
|
|
2504
|
-
const
|
|
2505
|
-
const body = await executeMikrotikCommand(cmd, ctx);
|
|
2513
|
+
const body = await executeMikrotikCommand(buildExportCommand(opts), ctx);
|
|
2506
2514
|
if (isEmpty(body) || looksLikeError(body)) {
|
|
2507
2515
|
return { ok: false, device, error: body.trim() || "(empty export)" };
|
|
2508
2516
|
}
|
|
2509
2517
|
const stamp = await deviceDateStamp(ctx);
|
|
2510
|
-
const name = `${device}_${stamp}${labelSlug(opts.label)}.rsc`;
|
|
2518
|
+
const name = `${deviceSlug(device)}_${stamp}${labelSlug(opts.label)}.rsc`;
|
|
2511
2519
|
return { ok: true, device, name: writeBackup(name, body), bytes: Buffer.byteLength(body) };
|
|
2512
2520
|
}
|
|
2513
2521
|
|
|
@@ -2578,13 +2586,22 @@ var localBackupTools = [
|
|
|
2578
2586
|
name: "create_local_backup",
|
|
2579
2587
|
title: "Create Local Config Backup (host vault)",
|
|
2580
2588
|
annotations: WRITE,
|
|
2581
|
-
description: "Captures the device's full configuration with `/export` and saves it as a timestamped " + "plain-text `.rsc` file in the MCP server's LOCAL backup vault (default " + "`~/.mikrotik-mcp/backups/`, override with the `MIKROTIK_BACKUP_DIR` env var) \u2014 NOT on the " + "device and NOT in S3. The filename is `<device>_<date_time>.rsc` stamped in the device's
|
|
2589
|
+
description: "Captures the device's full configuration with `/export` and saves it as a timestamped " + "plain-text `.rsc` file in the MCP server's LOCAL backup vault (default " + "`~/.mikrotik-mcp/backups/`, override with the `MIKROTIK_BACKUP_DIR` env var) \u2014 NOT on the " + "device and NOT in S3. The filename is `<device-slug>_<date_time>.rsc` \u2014 the device name is " + "slugified (spaces/underscores/etc \u2192 dash) \u2014 stamped in the device's local clock (24-hour; " + "Jalali for the Tehran timezone, Gregorian otherwise). This is a host-side, human-readable, " + "diffable copy you can restore later with restore_local_backup. Compare: create_backup makes " + "a binary `/system backup` file ON the device; capture_config_snapshot stores an export in " + "the local snapshot database; upload_backup_to_s3 pushes to S3. ALWAYS default to a FULL " + "backup of the complete configuration: call this with no option flags (the bare `/export`) " + "unless the user explicitly asks for less or more. Narrow it ONLY on request \u2014 set compact to " + "drop default values, or use export_section for a single subsection. Broaden it ONLY on " + "request \u2014 set verbose to include every parameter (even defaults), show_sensitive to include " + "secrets (keys/passwords), or use create_backup for a binary full-system snapshot. terse just " + "changes the text to one machine-readable line per item. Returns the saved filename, byte size " + "and vault path.",
|
|
2582
2590
|
inputSchema: {
|
|
2583
2591
|
label: z6.string().optional().describe('Optional label appended to the filename, e.g. "pre-upgrade".'),
|
|
2584
|
-
show_sensitive: z6.boolean().default(false).describe("Include secrets (keys/passwords) in the export. Default false.")
|
|
2592
|
+
show_sensitive: z6.boolean().default(false).describe("Include secrets (keys/passwords) in the export. Default false."),
|
|
2593
|
+
verbose: z6.boolean().default(false).describe("Include every parameter, even defaults (RouterOS `verbose`)."),
|
|
2594
|
+
compact: z6.boolean().default(false).describe("Export only non-default values (RouterOS `compact`; ignored if verbose)."),
|
|
2595
|
+
terse: z6.boolean().default(false).describe("One self-contained, machine-readable line per item (RouterOS `terse`).")
|
|
2585
2596
|
},
|
|
2586
2597
|
async handler(a, ctx) {
|
|
2587
|
-
const r = await createLocalBackup(ctx, {
|
|
2598
|
+
const r = await createLocalBackup(ctx, {
|
|
2599
|
+
label: a.label,
|
|
2600
|
+
showSensitive: a.show_sensitive,
|
|
2601
|
+
verbose: a.verbose,
|
|
2602
|
+
compact: a.compact,
|
|
2603
|
+
terse: a.terse
|
|
2604
|
+
});
|
|
2588
2605
|
if (!r.ok)
|
|
2589
2606
|
return `Failed to capture export for a local backup: ${r.error}`;
|
|
2590
2607
|
return `Saved local backup '${r.name}' (${r.bytes} bytes) for device '${r.device}' to ` + `${backupDir()}. Restore it with restore_local_backup name=${r.name}.`;
|
|
@@ -2720,7 +2737,7 @@ function trimSlashes(s) {
|
|
|
2720
2737
|
return s.replace(/^\/+|\/+$/g, "");
|
|
2721
2738
|
}
|
|
2722
2739
|
function s3DevicePrefix(device) {
|
|
2723
|
-
const segments = [getS3Config()?.prefix ?? "", device
|
|
2740
|
+
const segments = [getS3Config()?.prefix ?? "", device ? deviceSlug(device) : ""].map(trimSlashes).filter(Boolean);
|
|
2724
2741
|
return segments.length ? `${segments.join("/")}/` : "";
|
|
2725
2742
|
}
|
|
2726
2743
|
function s3Key(name, device) {
|
|
@@ -22171,7 +22188,7 @@ function selectToolModules(filter = {}, catalog = moduleCatalog) {
|
|
|
22171
22188
|
// package.json
|
|
22172
22189
|
var package_default = {
|
|
22173
22190
|
name: "@usex/mikrotik-mcp",
|
|
22174
|
-
version: "3.
|
|
22191
|
+
version: "3.19.0",
|
|
22175
22192
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
22176
22193
|
keywords: [
|
|
22177
22194
|
"ai",
|