@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 CHANGED
@@ -106,7 +106,7 @@ import {
106
106
  updateAaaEntity,
107
107
  updateSummaryLine,
108
108
  writeBackup
109
- } from "./shared/cli-hfhmh1qe.js";
109
+ } from "./shared/cli-38m6waxx.js";
110
110
 
111
111
  // src/cli.ts
112
112
  import { existsSync as existsSync2 } from "fs";
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  selectToolModules,
30
30
  setConfig,
31
31
  updateSummaryLine
32
- } from "./shared/library-399v2azd.js";
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-6xcbh1kn.js");
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))
@@ -4,7 +4,7 @@ import {
4
4
  allToolModules,
5
5
  moduleCatalog,
6
6
  selectToolModules
7
- } from "./cli-hfhmh1qe.js";
7
+ } from "./cli-38m6waxx.js";
8
8
  export {
9
9
  selectToolModules,
10
10
  moduleCatalog,
@@ -4,7 +4,7 @@ import {
4
4
  allToolModules,
5
5
  moduleCatalog,
6
6
  selectToolModules
7
- } from "./library-399v2azd.js";
7
+ } from "./library-fs4nps73.js";
8
8
  export {
9
9
  selectToolModules,
10
10
  moduleCatalog,
@@ -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-8hq0qqwm.js");
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))