@usex/mikrotik-mcp 4.5.0 → 4.7.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 +1 -1
- package/dist/index.js +1 -1
- package/dist/shared/{cli-hfhmh1qe.js → cli-38m6waxx.js} +41 -5
- package/dist/shared/{cli-6xcbh1kn.js → cli-jd7xqp2f.js} +1 -1
- package/dist/shared/{library-8hq0qqwm.js → library-31wz8hdh.js} +1 -1
- package/dist/shared/{library-399v2azd.js → library-fs4nps73.js} +41 -5
- package/dist/ui/observability.html +44 -43
- package/package.json +1 -1
- package/schemas/tool-catalog.json +17 -5
- package/schemas/tools/add_ip_address.json +7 -1
- package/schemas/tools/add_ipv6_address.json +7 -1
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
selectToolModules,
|
|
30
30
|
setConfig,
|
|
31
31
|
updateSummaryLine
|
|
32
|
-
} from "./shared/library-
|
|
32
|
+
} from "./shared/library-fs4nps73.js";
|
|
33
33
|
// src/server.ts
|
|
34
34
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
35
35
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -8489,7 +8489,7 @@ var cache = null;
|
|
|
8489
8489
|
async function gateway() {
|
|
8490
8490
|
if (cache)
|
|
8491
8491
|
return cache;
|
|
8492
|
-
const { moduleCatalog } = await import("./cli-
|
|
8492
|
+
const { moduleCatalog } = await import("./cli-jd7xqp2f.js");
|
|
8493
8493
|
const forIndex = [];
|
|
8494
8494
|
const byName = new Map;
|
|
8495
8495
|
for (const mod of moduleCatalog) {
|
|
@@ -12247,22 +12247,51 @@ ${details}`;
|
|
|
12247
12247
|
|
|
12248
12248
|
// src/tools/ip-address.ts
|
|
12249
12249
|
import { z as z34 } from "zod";
|
|
12250
|
+
|
|
12251
|
+
// src/utils/ip-overlap.ts
|
|
12252
|
+
function cidrsOverlap(a, b) {
|
|
12253
|
+
return cidrContains(a, b) || cidrContains(b, a);
|
|
12254
|
+
}
|
|
12255
|
+
async function findAddressConflicts(basePath, address, ctx) {
|
|
12256
|
+
const existing = await executeMikrotikCommand(`${basePath} print terse`, ctx);
|
|
12257
|
+
const conflicts = [];
|
|
12258
|
+
for (const line of existing.split(`
|
|
12259
|
+
`)) {
|
|
12260
|
+
const addr = line.match(/address=([^\s]+)/)?.[1];
|
|
12261
|
+
if (!addr)
|
|
12262
|
+
continue;
|
|
12263
|
+
if (cidrsOverlap(address, addr)) {
|
|
12264
|
+
const iface = line.match(/interface=([^\s]+)/)?.[1] ?? "?";
|
|
12265
|
+
conflicts.push(`${addr} on ${iface}`);
|
|
12266
|
+
}
|
|
12267
|
+
}
|
|
12268
|
+
return conflicts;
|
|
12269
|
+
}
|
|
12270
|
+
|
|
12271
|
+
// src/tools/ip-address.ts
|
|
12250
12272
|
var ipAddressTools = [
|
|
12251
12273
|
defineTool({
|
|
12252
12274
|
name: "add_ip_address",
|
|
12253
12275
|
title: "Add IPv4 Address to Interface",
|
|
12254
12276
|
annotations: WRITE,
|
|
12255
|
-
description: "Assigns an IPv4 address to an interface (`/ip address add`). Use this to add a CIDR" + " address (e.g. '192.168.1.1/24') as the router's own address on a network segment \u2014" + " sets the interface's local address, optional network, and broadcast. For IPv6 use" + " add_ipv6_address. Returns the full detail of the new address entry including its `.id`.",
|
|
12277
|
+
description: "Assigns an IPv4 address to an interface (`/ip address add`). Use this to add a CIDR" + " address (e.g. '192.168.1.1/24') as the router's own address on a network segment \u2014" + " sets the interface's local address, optional network, and broadcast. For IPv6 use" + " add_ipv6_address. Before creating, this checks the existing `/ip address` list and" + " REFUSES to add an address that duplicates or overlaps an already-assigned subnet," + " returning the conflicting entries so you can pick a non-overlapping address. To" + " intentionally add an overlapping/secondary address, set `allow_overlap: true`." + " Returns the full detail of the new address entry including its `.id`.",
|
|
12256
12278
|
inputSchema: {
|
|
12257
12279
|
address: z34.string().describe("Address with CIDR, e.g. '192.168.1.1/24'"),
|
|
12258
12280
|
interface: z34.string(),
|
|
12259
12281
|
network: z34.string().optional(),
|
|
12260
12282
|
broadcast: z34.string().optional(),
|
|
12261
12283
|
comment: z34.string().optional(),
|
|
12262
|
-
disabled: z34.boolean().default(false)
|
|
12284
|
+
disabled: z34.boolean().default(false),
|
|
12285
|
+
allow_overlap: z34.boolean().default(false).describe("Bypass the duplicate/overlap guard to add a secondary address in an existing subnet")
|
|
12263
12286
|
},
|
|
12264
12287
|
async handler(a, ctx) {
|
|
12265
12288
|
ctx.info(`Adding IP address: address=${a.address}, interface=${a.interface}`);
|
|
12289
|
+
if (!a.allow_overlap) {
|
|
12290
|
+
const conflicts = await findAddressConflicts("/ip address", a.address, ctx);
|
|
12291
|
+
if (conflicts.length > 0) {
|
|
12292
|
+
return `Refused: ${a.address} duplicates or overlaps ${conflicts.length} existing` + ` address(es): ${conflicts.join(", ")}. Choose a non-overlapping address, or` + ` re-run with allow_overlap=true to add it intentionally.`;
|
|
12293
|
+
}
|
|
12294
|
+
}
|
|
12266
12295
|
const cmd = new Cmd("/ip address add").set("address", a.address).set("interface", a.interface).opt("network", a.network).opt("broadcast", a.broadcast).opt("comment", a.comment).flag("disabled", a.disabled).build();
|
|
12267
12296
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
12268
12297
|
if (looksLikeError(result))
|
|
@@ -13000,7 +13029,7 @@ var ipv6AddressTools = [
|
|
|
13000
13029
|
name: "add_ipv6_address",
|
|
13001
13030
|
title: "Add IPv6 Address to Interface",
|
|
13002
13031
|
annotations: WRITE,
|
|
13003
|
-
description: "Assigns an IPv6 address to an interface (`/ipv6 address add`) \u2014 use this to give an interface a static or pool-derived IPv6 address and optionally advertise the prefix via Router Advertisements (ND). " + "For IPv4 address assignment use add_ip_address. For IPv6 routing entries (next-hop/gateway records) use add_ipv6_route. " + "Returns the created entry's full detail including its `.id`.\n\n" + `Notes:
|
|
13032
|
+
description: "Assigns an IPv6 address to an interface (`/ipv6 address add`) \u2014 use this to give an interface a static or pool-derived IPv6 address and optionally advertise the prefix via Router Advertisements (ND). " + "For IPv4 address assignment use add_ip_address. For IPv6 routing entries (next-hop/gateway records) use add_ipv6_route. " + "Returns the created entry's full detail including its `.id`.\n\n" + "Before creating, this checks the existing `/ipv6 address` list and REFUSES to add an" + " address that duplicates or overlaps an already-assigned prefix, returning the" + " conflicting entries so you can pick a non-overlapping address. To intentionally add" + " an overlapping/secondary address set `allow_overlap: true`.\n\n" + `Notes:
|
|
13004
13033
|
` + ` address: IPv6 with prefix length, e.g. '2001:db8::1/64'. When from_pool
|
|
13005
13034
|
` + ` is set the host part may be omitted and is taken from the pool.
|
|
13006
13035
|
` + ` eui_64: derive the host part from the interface MAC (modified EUI-64).
|
|
@@ -13013,10 +13042,17 @@ var ipv6AddressTools = [
|
|
|
13013
13042
|
from_pool: z40.string().optional().describe("IPv6 pool name to take the prefix from"),
|
|
13014
13043
|
no_dad: z40.boolean().optional().describe("Skip Duplicate Address Detection for this address"),
|
|
13015
13044
|
comment: z40.string().optional(),
|
|
13016
|
-
disabled: z40.boolean().default(false)
|
|
13045
|
+
disabled: z40.boolean().default(false),
|
|
13046
|
+
allow_overlap: z40.boolean().default(false).describe("Bypass the duplicate/overlap guard to add a secondary address in an existing prefix")
|
|
13017
13047
|
},
|
|
13018
13048
|
async handler(a, ctx) {
|
|
13019
13049
|
ctx.info(`Adding IPv6 address: address=${a.address}, interface=${a.interface}`);
|
|
13050
|
+
if (!a.allow_overlap && !a.eui_64 && !a.from_pool) {
|
|
13051
|
+
const conflicts = await findAddressConflicts("/ipv6 address", a.address, ctx);
|
|
13052
|
+
if (conflicts.length > 0) {
|
|
13053
|
+
return `Refused: ${a.address} duplicates or overlaps ${conflicts.length} existing` + ` address(es): ${conflicts.join(", ")}. Choose a non-overlapping address, or` + ` re-run with allow_overlap=true to add it intentionally.`;
|
|
13054
|
+
}
|
|
13055
|
+
}
|
|
13020
13056
|
const cmd = new Cmd("/ipv6 address add").set("address", a.address).set("interface", a.interface).bool("advertise", a.advertise).bool("eui-64", a.eui_64).opt("from-pool", a.from_pool).bool("no-dad", a.no_dad).opt("comment", a.comment).flag("disabled", a.disabled).build();
|
|
13021
13057
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
13022
13058
|
if (looksLikeError(result))
|
|
@@ -8298,7 +8298,7 @@ var cache = null;
|
|
|
8298
8298
|
async function gateway() {
|
|
8299
8299
|
if (cache)
|
|
8300
8300
|
return cache;
|
|
8301
|
-
const { moduleCatalog } = await import("./library-
|
|
8301
|
+
const { moduleCatalog } = await import("./library-31wz8hdh.js");
|
|
8302
8302
|
const forIndex = [];
|
|
8303
8303
|
const byName = new Map;
|
|
8304
8304
|
for (const mod of moduleCatalog) {
|
|
@@ -12056,22 +12056,51 @@ ${details}`;
|
|
|
12056
12056
|
|
|
12057
12057
|
// src/tools/ip-address.ts
|
|
12058
12058
|
import { z as z34 } from "zod";
|
|
12059
|
+
|
|
12060
|
+
// src/utils/ip-overlap.ts
|
|
12061
|
+
function cidrsOverlap(a, b) {
|
|
12062
|
+
return cidrContains(a, b) || cidrContains(b, a);
|
|
12063
|
+
}
|
|
12064
|
+
async function findAddressConflicts(basePath, address, ctx) {
|
|
12065
|
+
const existing = await executeMikrotikCommand(`${basePath} print terse`, ctx);
|
|
12066
|
+
const conflicts = [];
|
|
12067
|
+
for (const line of existing.split(`
|
|
12068
|
+
`)) {
|
|
12069
|
+
const addr = line.match(/address=([^\s]+)/)?.[1];
|
|
12070
|
+
if (!addr)
|
|
12071
|
+
continue;
|
|
12072
|
+
if (cidrsOverlap(address, addr)) {
|
|
12073
|
+
const iface = line.match(/interface=([^\s]+)/)?.[1] ?? "?";
|
|
12074
|
+
conflicts.push(`${addr} on ${iface}`);
|
|
12075
|
+
}
|
|
12076
|
+
}
|
|
12077
|
+
return conflicts;
|
|
12078
|
+
}
|
|
12079
|
+
|
|
12080
|
+
// src/tools/ip-address.ts
|
|
12059
12081
|
var ipAddressTools = [
|
|
12060
12082
|
defineTool({
|
|
12061
12083
|
name: "add_ip_address",
|
|
12062
12084
|
title: "Add IPv4 Address to Interface",
|
|
12063
12085
|
annotations: WRITE,
|
|
12064
|
-
description: "Assigns an IPv4 address to an interface (`/ip address add`). Use this to add a CIDR" + " address (e.g. '192.168.1.1/24') as the router's own address on a network segment \u2014" + " sets the interface's local address, optional network, and broadcast. For IPv6 use" + " add_ipv6_address. Returns the full detail of the new address entry including its `.id`.",
|
|
12086
|
+
description: "Assigns an IPv4 address to an interface (`/ip address add`). Use this to add a CIDR" + " address (e.g. '192.168.1.1/24') as the router's own address on a network segment \u2014" + " sets the interface's local address, optional network, and broadcast. For IPv6 use" + " add_ipv6_address. Before creating, this checks the existing `/ip address` list and" + " REFUSES to add an address that duplicates or overlaps an already-assigned subnet," + " returning the conflicting entries so you can pick a non-overlapping address. To" + " intentionally add an overlapping/secondary address, set `allow_overlap: true`." + " Returns the full detail of the new address entry including its `.id`.",
|
|
12065
12087
|
inputSchema: {
|
|
12066
12088
|
address: z34.string().describe("Address with CIDR, e.g. '192.168.1.1/24'"),
|
|
12067
12089
|
interface: z34.string(),
|
|
12068
12090
|
network: z34.string().optional(),
|
|
12069
12091
|
broadcast: z34.string().optional(),
|
|
12070
12092
|
comment: z34.string().optional(),
|
|
12071
|
-
disabled: z34.boolean().default(false)
|
|
12093
|
+
disabled: z34.boolean().default(false),
|
|
12094
|
+
allow_overlap: z34.boolean().default(false).describe("Bypass the duplicate/overlap guard to add a secondary address in an existing subnet")
|
|
12072
12095
|
},
|
|
12073
12096
|
async handler(a, ctx) {
|
|
12074
12097
|
ctx.info(`Adding IP address: address=${a.address}, interface=${a.interface}`);
|
|
12098
|
+
if (!a.allow_overlap) {
|
|
12099
|
+
const conflicts = await findAddressConflicts("/ip address", a.address, ctx);
|
|
12100
|
+
if (conflicts.length > 0) {
|
|
12101
|
+
return `Refused: ${a.address} duplicates or overlaps ${conflicts.length} existing` + ` address(es): ${conflicts.join(", ")}. Choose a non-overlapping address, or` + ` re-run with allow_overlap=true to add it intentionally.`;
|
|
12102
|
+
}
|
|
12103
|
+
}
|
|
12075
12104
|
const cmd = new Cmd("/ip address add").set("address", a.address).set("interface", a.interface).opt("network", a.network).opt("broadcast", a.broadcast).opt("comment", a.comment).flag("disabled", a.disabled).build();
|
|
12076
12105
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
12077
12106
|
if (looksLikeError(result))
|
|
@@ -12809,7 +12838,7 @@ var ipv6AddressTools = [
|
|
|
12809
12838
|
name: "add_ipv6_address",
|
|
12810
12839
|
title: "Add IPv6 Address to Interface",
|
|
12811
12840
|
annotations: WRITE,
|
|
12812
|
-
description: "Assigns an IPv6 address to an interface (`/ipv6 address add`) \u2014 use this to give an interface a static or pool-derived IPv6 address and optionally advertise the prefix via Router Advertisements (ND). " + "For IPv4 address assignment use add_ip_address. For IPv6 routing entries (next-hop/gateway records) use add_ipv6_route. " + "Returns the created entry's full detail including its `.id`.\n\n" + `Notes:
|
|
12841
|
+
description: "Assigns an IPv6 address to an interface (`/ipv6 address add`) \u2014 use this to give an interface a static or pool-derived IPv6 address and optionally advertise the prefix via Router Advertisements (ND). " + "For IPv4 address assignment use add_ip_address. For IPv6 routing entries (next-hop/gateway records) use add_ipv6_route. " + "Returns the created entry's full detail including its `.id`.\n\n" + "Before creating, this checks the existing `/ipv6 address` list and REFUSES to add an" + " address that duplicates or overlaps an already-assigned prefix, returning the" + " conflicting entries so you can pick a non-overlapping address. To intentionally add" + " an overlapping/secondary address set `allow_overlap: true`.\n\n" + `Notes:
|
|
12813
12842
|
` + ` address: IPv6 with prefix length, e.g. '2001:db8::1/64'. When from_pool
|
|
12814
12843
|
` + ` is set the host part may be omitted and is taken from the pool.
|
|
12815
12844
|
` + ` eui_64: derive the host part from the interface MAC (modified EUI-64).
|
|
@@ -12822,10 +12851,17 @@ var ipv6AddressTools = [
|
|
|
12822
12851
|
from_pool: z40.string().optional().describe("IPv6 pool name to take the prefix from"),
|
|
12823
12852
|
no_dad: z40.boolean().optional().describe("Skip Duplicate Address Detection for this address"),
|
|
12824
12853
|
comment: z40.string().optional(),
|
|
12825
|
-
disabled: z40.boolean().default(false)
|
|
12854
|
+
disabled: z40.boolean().default(false),
|
|
12855
|
+
allow_overlap: z40.boolean().default(false).describe("Bypass the duplicate/overlap guard to add a secondary address in an existing prefix")
|
|
12826
12856
|
},
|
|
12827
12857
|
async handler(a, ctx) {
|
|
12828
12858
|
ctx.info(`Adding IPv6 address: address=${a.address}, interface=${a.interface}`);
|
|
12859
|
+
if (!a.allow_overlap && !a.eui_64 && !a.from_pool) {
|
|
12860
|
+
const conflicts = await findAddressConflicts("/ipv6 address", a.address, ctx);
|
|
12861
|
+
if (conflicts.length > 0) {
|
|
12862
|
+
return `Refused: ${a.address} duplicates or overlaps ${conflicts.length} existing` + ` address(es): ${conflicts.join(", ")}. Choose a non-overlapping address, or` + ` re-run with allow_overlap=true to add it intentionally.`;
|
|
12863
|
+
}
|
|
12864
|
+
}
|
|
12829
12865
|
const cmd = new Cmd("/ipv6 address add").set("address", a.address).set("interface", a.interface).bool("advertise", a.advertise).bool("eui-64", a.eui_64).opt("from-pool", a.from_pool).bool("no-dad", a.no_dad).opt("comment", a.comment).flag("disabled", a.disabled).build();
|
|
12830
12866
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
12831
12867
|
if (looksLikeError(result))
|