@usex/mikrotik-mcp 3.58.0 → 3.60.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.
Files changed (36) hide show
  1. package/dist/cli.js +31 -3
  2. package/dist/index.d.ts +5 -0
  3. package/dist/index.js +1 -1
  4. package/dist/shared/{cli-zq0rg72c.js → cli-7yjsmkwg.js} +1 -1
  5. package/dist/shared/{cli-r5p22dag.js → cli-ghatyv7e.js} +154 -88
  6. package/dist/shared/{library-41tn0b6b.js → library-1hs18tmh.js} +1 -1
  7. package/dist/shared/{library-84jwmz7a.js → library-7j22z9a8.js} +154 -88
  8. package/dist/ui/aaa.html +1 -1
  9. package/dist/ui/connected-devices.html +1 -1
  10. package/dist/ui/dashboard.html +1 -1
  11. package/dist/ui/firewall-audit.html +1 -1
  12. package/dist/ui/firewall.html +1 -1
  13. package/dist/ui/interfaces.html +1 -1
  14. package/dist/ui/observability.html +1 -1
  15. package/dist/ui/records.html +1 -1
  16. package/package.json +1 -1
  17. package/schemas/README.md +5 -5
  18. package/schemas/config.schema.json +47 -10
  19. package/schemas/tool-catalog.json +2941 -736
  20. package/schemas/tools/check_server_pulse.json +12 -0
  21. package/schemas/tools/config_check_drift.json +5 -1
  22. package/schemas/tools/config_reconcile.json +3 -1
  23. package/schemas/tools/config_set_baseline.json +4 -1
  24. package/schemas/tools/correlate_events.json +4 -1
  25. package/schemas/tools/diagnose.json +3 -1
  26. package/schemas/tools/get_filter_rule.json +1 -1
  27. package/schemas/tools/memory_add_observations.json +7 -2
  28. package/schemas/tools/memory_create_entities.json +7 -2
  29. package/schemas/tools/memory_create_relations.json +8 -2
  30. package/schemas/tools/memory_delete_entities.json +3 -1
  31. package/schemas/tools/memory_delete_observations.json +7 -2
  32. package/schemas/tools/memory_delete_relations.json +8 -2
  33. package/schemas/tools/memory_open_nodes.json +3 -1
  34. package/schemas/tools/memory_search_nodes.json +3 -1
  35. package/schemas/tools/suggest_fix.json +3 -1
  36. package/schemas/tools/trace_path.json +5 -1
package/dist/cli.js CHANGED
@@ -106,7 +106,7 @@ import {
106
106
  updateAaaEntity,
107
107
  updateSummaryLine,
108
108
  writeBackup
109
- } from "./shared/cli-r5p22dag.js";
109
+ } from "./shared/cli-ghatyv7e.js";
110
110
 
111
111
  // src/cli.ts
112
112
  import { existsSync as existsSync2 } from "fs";
@@ -1765,12 +1765,12 @@ async function configRoutes(req, url, admin) {
1765
1765
  }
1766
1766
  async function modulesRoutes(req, url) {
1767
1767
  const p = url.pathname;
1768
- if (p !== "/api/modules" && p !== "/api/modules/toggle")
1768
+ if (p !== "/api/modules" && p !== "/api/modules/toggle" && p !== "/api/modules/app-views")
1769
1769
  return null;
1770
1770
  if (p === "/api/modules" && req.method === "GET") {
1771
1771
  const cfg = getConfig();
1772
1772
  const src = getConfigSource();
1773
- return json3({ ...moduleSurface(cfg.tools), filter: cfg.tools, source: src });
1773
+ return json3({ ...moduleSurface(cfg.tools), filter: cfg.tools, source: src, appViews: cfg.mcp.appViews });
1774
1774
  }
1775
1775
  if (p === "/api/modules/toggle" && req.method === "POST") {
1776
1776
  const b = await readJson(req);
@@ -1807,6 +1807,34 @@ async function modulesRoutes(req, url) {
1807
1807
  source: getConfigSource()
1808
1808
  });
1809
1809
  }
1810
+ if (p === "/api/modules/app-views" && req.method === "POST") {
1811
+ const b = await readJson(req);
1812
+ if (typeof b?.enabled !== "boolean") {
1813
+ return json3({ error: "enabled (boolean) is required" }, 400);
1814
+ }
1815
+ const cfg = getConfig();
1816
+ const next = { ...cfg, mcp: { ...cfg.mcp, appViews: b.enabled } };
1817
+ setConfig(next);
1818
+ let persisted = true;
1819
+ let warning;
1820
+ try {
1821
+ atomicWrite(getConfigSource().path, serializeConfig(next));
1822
+ } catch (e) {
1823
+ persisted = false;
1824
+ warning = `applied live but not saved to disk: ${e instanceof Error ? e.message : String(e)}`;
1825
+ }
1826
+ if (persisted) {
1827
+ recordVersion(getConfig(), "auto", Date.now(), `app views ${b.enabled ? "enabled" : "disabled"}`);
1828
+ }
1829
+ return json3({
1830
+ ok: true,
1831
+ persisted,
1832
+ requiresReconnect: true,
1833
+ warning,
1834
+ appViews: b.enabled,
1835
+ source: getConfigSource()
1836
+ });
1837
+ }
1810
1838
  return null;
1811
1839
  }
1812
1840
  async function captureRoutes(req, url) {
package/dist/index.d.ts CHANGED
@@ -136,6 +136,11 @@ interface ToolDef<Shape extends ZodRawShape> {
136
136
  * view (it still returns `text` for non-UI hosts).
137
137
  */
138
138
  ui?: UiLink;
139
+ /**
140
+ * When true, skip the multi-device `device` selector injection for this tool.
141
+ * Use for server-introspection tools that never contact a RouterOS device.
142
+ */
143
+ noDevice?: boolean;
139
144
  /** Handler returning the result shown to the model (text, or text + structured data). */
140
145
  handler: (args: any, ctx: ToolContext) => Promise<HandlerOutput> | HandlerOutput;
141
146
  }
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  selectToolModules,
30
30
  setConfig,
31
31
  updateSummaryLine
32
- } from "./shared/library-84jwmz7a.js";
32
+ } from "./shared/library-7j22z9a8.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";
@@ -4,7 +4,7 @@ import {
4
4
  allToolModules,
5
5
  moduleCatalog,
6
6
  selectToolModules
7
- } from "./cli-r5p22dag.js";
7
+ } from "./cli-ghatyv7e.js";
8
8
  export {
9
9
  selectToolModules,
10
10
  moduleCatalog,
@@ -310,7 +310,7 @@ var McpServerSettingsSchema = z.object({
310
310
  allowedOrigins: z.string().default(""),
311
311
  corsOrigins: z.string().default(""),
312
312
  toolPageSize: z.coerce.number().int().min(0).default(0),
313
- appViews: z.boolean().default(true)
313
+ appViews: z.boolean().default(false)
314
314
  });
315
315
  var JumpHostSchema = z.object({
316
316
  host: z.string(),
@@ -2478,7 +2478,7 @@ function defineTool(def) {
2478
2478
  handler: def.handler,
2479
2479
  register(server, opts = {}) {
2480
2480
  const { sendLog, deviceNames, deviceAliases, deviceDirectory: deviceDirectory2, appViews } = opts;
2481
- const multiDevice = !!deviceNames && deviceNames.length > 1;
2481
+ const multiDevice = !def.noDevice && !!deviceNames && deviceNames.length > 1;
2482
2482
  const { ui, auto } = appViews === false ? { ui: undefined, auto: false } : effectiveUi(def);
2483
2483
  const selectorNames = multiDevice && deviceNames ? [...new Set([...deviceNames, ...deviceAliases ?? []])] : [];
2484
2484
  let inputSchema = multiDevice ? {
@@ -8208,7 +8208,7 @@ var rawCommandTools = [
8208
8208
  if (!cmd)
8209
8209
  return "Provide a RouterOS command to run.";
8210
8210
  ctx.info(`Running raw RouterOS command: ${cmd}`);
8211
- const result = await executeMikrotikCommand(cmd, ctx);
8211
+ const result = await executeMikrotikCommand(cmd, ctx, { maxMs: 30000 });
8212
8212
  if (looksLikeError(result))
8213
8213
  return `RouterOS command error: ${result}`;
8214
8214
  return result.trim() ? result : "(command completed with no output)";
@@ -8330,7 +8330,7 @@ var cache = null;
8330
8330
  async function gateway() {
8331
8331
  if (cache)
8332
8332
  return cache;
8333
- const { moduleCatalog } = await import("./cli-zq0rg72c.js");
8333
+ const { moduleCatalog } = await import("./cli-7yjsmkwg.js");
8334
8334
  const forIndex = [];
8335
8335
  const byName = new Map;
8336
8336
  for (const mod of moduleCatalog) {
@@ -10527,7 +10527,30 @@ ${results.join(`
10527
10527
 
10528
10528
  // src/tools/firewall-nat.ts
10529
10529
  import { z as z29 } from "zod";
10530
+
10531
+ // src/tools/_resolve-rule-id.ts
10532
+ function ruleResolver(scope) {
10533
+ return async function resolveRuleId(ruleId, ctx) {
10534
+ if (/^\d+$/.test(ruleId)) {
10535
+ const id = `*${ruleId}`;
10536
+ const byId = await executeMikrotikCommand(`${scope} print count-only where .id=${id}`, ctx);
10537
+ if (byId.trim() !== "0")
10538
+ return id;
10539
+ const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
10540
+ if (isEmpty(idsRaw))
10541
+ return null;
10542
+ const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
10543
+ const pos = Number.parseInt(ruleId, 10);
10544
+ return pos >= 0 && pos < ids.length ? ids[pos] : null;
10545
+ }
10546
+ const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
10547
+ return count.trim() !== "0" ? ruleId : null;
10548
+ };
10549
+ }
10550
+
10551
+ // src/tools/firewall-nat.ts
10530
10552
  var isDigits2 = (s) => /^\d+$/.test(s);
10553
+ var resolveNatRuleId = ruleResolver("/ip firewall nat");
10531
10554
  async function updateNatRule(a, ctx) {
10532
10555
  ctx.info(`Updating NAT rule: rule_id=${a.rule_id}`);
10533
10556
  const updates = [];
@@ -10603,11 +10626,14 @@ async function updateNatRule(a, ctx) {
10603
10626
  }
10604
10627
  if (updates.length === 0)
10605
10628
  return "No updates specified.";
10606
- const cmd = `/ip firewall nat set ${a.rule_id} ${updates.join(" ")}`;
10629
+ const id = await resolveNatRuleId(a.rule_id, ctx);
10630
+ if (!id)
10631
+ return `NAT rule '${a.rule_id}' not found.`;
10632
+ const cmd = `/ip firewall nat set ${id} ${updates.join(" ")}`;
10607
10633
  const result = await executeMikrotikCommand(cmd, ctx);
10608
10634
  if (looksLikeError(result))
10609
10635
  return `Failed to update NAT rule: ${result}`;
10610
- const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${a.rule_id}`, ctx);
10636
+ const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${id}`, ctx);
10611
10637
  return `NAT rule updated successfully:
10612
10638
 
10613
10639
  ${details}`;
@@ -10785,8 +10811,11 @@ ${result}`;
10785
10811
  },
10786
10812
  async handler(a, ctx) {
10787
10813
  ctx.info(`Getting NAT rule details: rule_id=${a.rule_id}`);
10788
- const result = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${a.rule_id}`, ctx);
10789
- return isEmpty(result) ? `NAT rule with ID '${a.rule_id}' not found.` : `NAT RULE DETAILS:
10814
+ const id = await resolveNatRuleId(a.rule_id, ctx);
10815
+ if (!id)
10816
+ return `NAT rule '${a.rule_id}' not found.`;
10817
+ const result = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${id}`, ctx);
10818
+ return isEmpty(result) ? `NAT rule '${a.rule_id}' not found.` : `NAT RULE DETAILS:
10790
10819
 
10791
10820
  ${result}`;
10792
10821
  }
@@ -10868,13 +10897,13 @@ ${result}`;
10868
10897
  inputSchema: { rule_id: z29.string() },
10869
10898
  async handler(a, ctx) {
10870
10899
  ctx.info(`Removing NAT rule: rule_id=${a.rule_id}`);
10871
- const count = await executeMikrotikCommand(`/ip firewall nat print count-only where .id=${a.rule_id}`, ctx);
10872
- if (count.trim() === "0")
10873
- return `NAT rule with ID '${a.rule_id}' not found.`;
10874
- const result = await executeMikrotikCommand(`/ip firewall nat remove ${a.rule_id}`, ctx);
10900
+ const id = await resolveNatRuleId(a.rule_id, ctx);
10901
+ if (!id)
10902
+ return `NAT rule '${a.rule_id}' not found.`;
10903
+ const result = await executeMikrotikCommand(`/ip firewall nat remove ${id}`, ctx);
10875
10904
  if (looksLikeError(result))
10876
10905
  return `Failed to remove NAT rule: ${result}`;
10877
- return `NAT rule with ID '${a.rule_id}' removed successfully.`;
10906
+ return `NAT rule '${a.rule_id}' (${id}) removed successfully.`;
10878
10907
  }
10879
10908
  }),
10880
10909
  defineTool({
@@ -10888,13 +10917,13 @@ ${result}`;
10888
10917
  },
10889
10918
  async handler(a, ctx) {
10890
10919
  ctx.info(`Moving NAT rule: rule_id=${a.rule_id} to position ${a.destination}`);
10891
- const count = await executeMikrotikCommand(`/ip firewall nat print count-only where .id=${a.rule_id}`, ctx);
10892
- if (count.trim() === "0")
10893
- return `NAT rule with ID '${a.rule_id}' not found.`;
10894
- const result = await executeMikrotikCommand(`/ip firewall nat move ${a.rule_id} destination=${a.destination}`, ctx);
10920
+ const id = await resolveNatRuleId(a.rule_id, ctx);
10921
+ if (!id)
10922
+ return `NAT rule '${a.rule_id}' not found.`;
10923
+ const result = await executeMikrotikCommand(`/ip firewall nat move ${id} destination=${a.destination}`, ctx);
10895
10924
  if (looksLikeError(result))
10896
10925
  return `Failed to move NAT rule: ${result}`;
10897
- return `NAT rule with ID '${a.rule_id}' moved to position ${a.destination}.`;
10926
+ return `NAT rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
10898
10927
  }
10899
10928
  }),
10900
10929
  defineTool({
@@ -10922,6 +10951,7 @@ ${result}`;
10922
10951
  // src/tools/firewall-mangle.ts
10923
10952
  import { z as z30 } from "zod";
10924
10953
  var isDigits3 = (s) => /^\d+$/.test(s);
10954
+ var resolveMangleRuleId = ruleResolver("/ip firewall mangle");
10925
10955
  async function updateMangleRule(a, ctx) {
10926
10956
  ctx.info(`Updating mangle rule: rule_id=${a.rule_id}`);
10927
10957
  const updates = [];
@@ -11007,11 +11037,14 @@ async function updateMangleRule(a, ctx) {
11007
11037
  }
11008
11038
  if (updates.length === 0)
11009
11039
  return "No updates specified.";
11010
- const cmd = `/ip firewall mangle set ${a.rule_id} ${updates.join(" ")}`;
11040
+ const id = await resolveMangleRuleId(a.rule_id, ctx);
11041
+ if (!id)
11042
+ return `Mangle rule '${a.rule_id}' not found.`;
11043
+ const cmd = `/ip firewall mangle set ${id} ${updates.join(" ")}`;
11011
11044
  const result = await executeMikrotikCommand(cmd, ctx);
11012
11045
  if (looksLikeError(result))
11013
11046
  return `Failed to update mangle rule: ${result}`;
11014
- const details = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${a.rule_id}`, ctx);
11047
+ const details = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${id}`, ctx);
11015
11048
  return `Mangle rule updated successfully:
11016
11049
 
11017
11050
  ${details}`;
@@ -11186,8 +11219,11 @@ ${result}`;
11186
11219
  },
11187
11220
  async handler(a, ctx) {
11188
11221
  ctx.info(`Getting mangle rule details: rule_id=${a.rule_id}`);
11189
- const result = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${a.rule_id}`, ctx);
11190
- return isEmpty(result) ? `Mangle rule with ID '${a.rule_id}' not found.` : `MANGLE RULE DETAILS:
11222
+ const id = await resolveMangleRuleId(a.rule_id, ctx);
11223
+ if (!id)
11224
+ return `Mangle rule '${a.rule_id}' not found.`;
11225
+ const result = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${id}`, ctx);
11226
+ return isEmpty(result) ? `Mangle rule '${a.rule_id}' not found.` : `MANGLE RULE DETAILS:
11191
11227
 
11192
11228
  ${result}`;
11193
11229
  }
@@ -11278,13 +11314,13 @@ ${result}`;
11278
11314
  inputSchema: { rule_id: z30.string() },
11279
11315
  async handler(a, ctx) {
11280
11316
  ctx.info(`Removing mangle rule: rule_id=${a.rule_id}`);
11281
- const count = await executeMikrotikCommand(`/ip firewall mangle print count-only where .id=${a.rule_id}`, ctx);
11282
- if (count.trim() === "0")
11283
- return `Mangle rule with ID '${a.rule_id}' not found.`;
11284
- const result = await executeMikrotikCommand(`/ip firewall mangle remove ${a.rule_id}`, ctx);
11317
+ const id = await resolveMangleRuleId(a.rule_id, ctx);
11318
+ if (!id)
11319
+ return `Mangle rule '${a.rule_id}' not found.`;
11320
+ const result = await executeMikrotikCommand(`/ip firewall mangle remove ${id}`, ctx);
11285
11321
  if (looksLikeError(result))
11286
11322
  return `Failed to remove mangle rule: ${result}`;
11287
- return `Mangle rule with ID '${a.rule_id}' removed successfully.`;
11323
+ return `Mangle rule '${a.rule_id}' (${id}) removed successfully.`;
11288
11324
  }
11289
11325
  }),
11290
11326
  defineTool({
@@ -11298,13 +11334,13 @@ ${result}`;
11298
11334
  },
11299
11335
  async handler(a, ctx) {
11300
11336
  ctx.info(`Moving mangle rule: rule_id=${a.rule_id} to position ${a.destination}`);
11301
- const count = await executeMikrotikCommand(`/ip firewall mangle print count-only where .id=${a.rule_id}`, ctx);
11302
- if (count.trim() === "0")
11303
- return `Mangle rule with ID '${a.rule_id}' not found.`;
11304
- const result = await executeMikrotikCommand(`/ip firewall mangle move ${a.rule_id} destination=${a.destination}`, ctx);
11337
+ const id = await resolveMangleRuleId(a.rule_id, ctx);
11338
+ if (!id)
11339
+ return `Mangle rule '${a.rule_id}' not found.`;
11340
+ const result = await executeMikrotikCommand(`/ip firewall mangle move ${id} destination=${a.destination}`, ctx);
11305
11341
  if (looksLikeError(result))
11306
11342
  return `Failed to move mangle rule: ${result}`;
11307
- return `Mangle rule with ID '${a.rule_id}' moved to position ${a.destination}.`;
11343
+ return `Mangle rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
11308
11344
  }
11309
11345
  }),
11310
11346
  defineTool({
@@ -13440,6 +13476,7 @@ ${result}`;
13440
13476
  // src/tools/ipv6-firewall-filter.ts
13441
13477
  import { z as z44 } from "zod";
13442
13478
  var isDigits4 = (s) => /^\d+$/.test(s);
13479
+ var resolveRuleId = ruleResolver("/ipv6 firewall filter");
13443
13480
  async function updateFilterRule2(a, ctx) {
13444
13481
  ctx.info(`Updating IPv6 firewall filter rule: rule_id=${a.rule_id}`);
13445
13482
  const updates = [];
@@ -13508,11 +13545,14 @@ async function updateFilterRule2(a, ctx) {
13508
13545
  }
13509
13546
  if (updates.length === 0)
13510
13547
  return "No updates specified.";
13511
- const cmd = `/ipv6 firewall filter set ${a.rule_id} ${updates.join(" ")}`;
13548
+ const id = await resolveRuleId(a.rule_id, ctx);
13549
+ if (!id)
13550
+ return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
13551
+ const cmd = `/ipv6 firewall filter set ${id} ${updates.join(" ")}`;
13512
13552
  const result = await executeMikrotikCommand(cmd, ctx);
13513
13553
  if (looksLikeError(result))
13514
13554
  return `Failed to update IPv6 firewall filter rule: ${result}`;
13515
- const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${a.rule_id}`, ctx);
13555
+ const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${id}`, ctx);
13516
13556
  return `IPv6 firewall filter rule updated successfully:
13517
13557
 
13518
13558
  ${details}`;
@@ -13669,8 +13709,11 @@ ${result}`;
13669
13709
  },
13670
13710
  async handler(a, ctx) {
13671
13711
  ctx.info(`Getting IPv6 firewall filter rule details: rule_id=${a.rule_id}`);
13672
- const result = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${a.rule_id}`, ctx);
13673
- return isEmpty(result) ? `IPv6 firewall filter rule with ID '${a.rule_id}' not found.` : `IPV6 FIREWALL FILTER RULE DETAILS:
13712
+ const id = await resolveRuleId(a.rule_id, ctx);
13713
+ if (!id)
13714
+ return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
13715
+ const result = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${id}`, ctx);
13716
+ return isEmpty(result) ? `IPv6 firewall filter rule '${a.rule_id}' not found.` : `IPV6 FIREWALL FILTER RULE DETAILS:
13674
13717
 
13675
13718
  ${result}`;
13676
13719
  }
@@ -13746,13 +13789,13 @@ ${result}`;
13746
13789
  inputSchema: { rule_id: z44.string() },
13747
13790
  async handler(a, ctx) {
13748
13791
  ctx.info(`Removing IPv6 firewall filter rule: rule_id=${a.rule_id}`);
13749
- const count = await executeMikrotikCommand(`/ipv6 firewall filter print count-only where .id=${a.rule_id}`, ctx);
13750
- if (count.trim() === "0")
13751
- return `IPv6 firewall filter rule with ID '${a.rule_id}' not found.`;
13752
- const result = await executeMikrotikCommand(`/ipv6 firewall filter remove ${a.rule_id}`, ctx);
13792
+ const id = await resolveRuleId(a.rule_id, ctx);
13793
+ if (!id)
13794
+ return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
13795
+ const result = await executeMikrotikCommand(`/ipv6 firewall filter remove ${id}`, ctx);
13753
13796
  if (looksLikeError(result))
13754
13797
  return `Failed to remove IPv6 firewall filter rule: ${result}`;
13755
- return `IPv6 firewall filter rule with ID '${a.rule_id}' removed successfully.`;
13798
+ return `IPv6 firewall filter rule '${a.rule_id}' (${id}) removed successfully.`;
13756
13799
  }
13757
13800
  }),
13758
13801
  defineTool({
@@ -13766,13 +13809,13 @@ ${result}`;
13766
13809
  },
13767
13810
  async handler(a, ctx) {
13768
13811
  ctx.info(`Moving IPv6 firewall filter rule: rule_id=${a.rule_id} to position ${a.destination}`);
13769
- const count = await executeMikrotikCommand(`/ipv6 firewall filter print count-only where .id=${a.rule_id}`, ctx);
13770
- if (count.trim() === "0")
13771
- return `IPv6 firewall filter rule with ID '${a.rule_id}' not found.`;
13772
- const result = await executeMikrotikCommand(`/ipv6 firewall filter move ${a.rule_id} destination=${a.destination}`, ctx);
13812
+ const id = await resolveRuleId(a.rule_id, ctx);
13813
+ if (!id)
13814
+ return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
13815
+ const result = await executeMikrotikCommand(`/ipv6 firewall filter move ${id} destination=${a.destination}`, ctx);
13773
13816
  if (looksLikeError(result))
13774
13817
  return `Failed to move IPv6 firewall filter rule: ${result}`;
13775
- return `IPv6 firewall filter rule with ID '${a.rule_id}' moved to position ${a.destination}.`;
13818
+ return `IPv6 firewall filter rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
13776
13819
  }
13777
13820
  }),
13778
13821
  defineTool({
@@ -13800,6 +13843,7 @@ ${result}`;
13800
13843
  // src/tools/ipv6-firewall-nat.ts
13801
13844
  import { z as z45 } from "zod";
13802
13845
  var isDigits5 = (s) => /^\d+$/.test(s);
13846
+ var resolveRuleId2 = ruleResolver("/ipv6 firewall nat");
13803
13847
  async function updateNatRule2(a, ctx) {
13804
13848
  ctx.info(`Updating IPv6 firewall NAT rule: rule_id=${a.rule_id}`);
13805
13849
  const updates = [];
@@ -13867,11 +13911,14 @@ async function updateNatRule2(a, ctx) {
13867
13911
  }
13868
13912
  if (updates.length === 0)
13869
13913
  return "No updates specified.";
13870
- const cmd = `/ipv6 firewall nat set ${a.rule_id} ${updates.join(" ")}`;
13914
+ const id = await resolveRuleId2(a.rule_id, ctx);
13915
+ if (!id)
13916
+ return `IPv6 NAT rule '${a.rule_id}' not found.`;
13917
+ const cmd = `/ipv6 firewall nat set ${id} ${updates.join(" ")}`;
13871
13918
  const result = await executeMikrotikCommand(cmd, ctx);
13872
13919
  if (looksLikeError(result))
13873
13920
  return `Failed to update IPv6 firewall NAT rule: ${result}`;
13874
- const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${a.rule_id}`, ctx);
13921
+ const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${id}`, ctx);
13875
13922
  return `IPv6 firewall NAT rule updated successfully:
13876
13923
 
13877
13924
  ${details}`;
@@ -14022,8 +14069,11 @@ ${result}`;
14022
14069
  },
14023
14070
  async handler(a, ctx) {
14024
14071
  ctx.info(`Getting IPv6 firewall NAT rule details: rule_id=${a.rule_id}`);
14025
- const result = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${a.rule_id}`, ctx);
14026
- return isEmpty(result) ? `IPv6 firewall NAT rule with ID '${a.rule_id}' not found.` : `IPV6 FIREWALL NAT RULE DETAILS:
14072
+ const id = await resolveRuleId2(a.rule_id, ctx);
14073
+ if (!id)
14074
+ return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
14075
+ const result = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${id}`, ctx);
14076
+ return isEmpty(result) ? `IPv6 firewall NAT rule '${a.rule_id}' not found.` : `IPV6 FIREWALL NAT RULE DETAILS:
14027
14077
 
14028
14078
  ${result}`;
14029
14079
  }
@@ -14098,13 +14148,13 @@ ${result}`;
14098
14148
  inputSchema: { rule_id: z45.string() },
14099
14149
  async handler(a, ctx) {
14100
14150
  ctx.info(`Removing IPv6 firewall NAT rule: rule_id=${a.rule_id}`);
14101
- const count = await executeMikrotikCommand(`/ipv6 firewall nat print count-only where .id=${a.rule_id}`, ctx);
14102
- if (count.trim() === "0")
14103
- return `IPv6 firewall NAT rule with ID '${a.rule_id}' not found.`;
14104
- const result = await executeMikrotikCommand(`/ipv6 firewall nat remove ${a.rule_id}`, ctx);
14151
+ const id = await resolveRuleId2(a.rule_id, ctx);
14152
+ if (!id)
14153
+ return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
14154
+ const result = await executeMikrotikCommand(`/ipv6 firewall nat remove ${id}`, ctx);
14105
14155
  if (looksLikeError(result))
14106
14156
  return `Failed to remove IPv6 firewall NAT rule: ${result}`;
14107
- return `IPv6 firewall NAT rule with ID '${a.rule_id}' removed successfully.`;
14157
+ return `IPv6 firewall NAT rule '${a.rule_id}' (${id}) removed successfully.`;
14108
14158
  }
14109
14159
  }),
14110
14160
  defineTool({
@@ -14118,13 +14168,13 @@ ${result}`;
14118
14168
  },
14119
14169
  async handler(a, ctx) {
14120
14170
  ctx.info(`Moving IPv6 firewall NAT rule: rule_id=${a.rule_id} to position ${a.destination}`);
14121
- const count = await executeMikrotikCommand(`/ipv6 firewall nat print count-only where .id=${a.rule_id}`, ctx);
14122
- if (count.trim() === "0")
14123
- return `IPv6 firewall NAT rule with ID '${a.rule_id}' not found.`;
14124
- const result = await executeMikrotikCommand(`/ipv6 firewall nat move ${a.rule_id} destination=${a.destination}`, ctx);
14171
+ const id = await resolveRuleId2(a.rule_id, ctx);
14172
+ if (!id)
14173
+ return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
14174
+ const result = await executeMikrotikCommand(`/ipv6 firewall nat move ${id} destination=${a.destination}`, ctx);
14125
14175
  if (looksLikeError(result))
14126
14176
  return `Failed to move IPv6 firewall NAT rule: ${result}`;
14127
- return `IPv6 firewall NAT rule with ID '${a.rule_id}' moved to position ${a.destination}.`;
14177
+ return `IPv6 firewall NAT rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
14128
14178
  }
14129
14179
  }),
14130
14180
  defineTool({
@@ -14152,6 +14202,7 @@ ${result}`;
14152
14202
  // src/tools/ipv6-firewall-mangle.ts
14153
14203
  import { z as z46 } from "zod";
14154
14204
  var isDigits6 = (s) => /^\d+$/.test(s);
14205
+ var resolveRuleId3 = ruleResolver("/ipv6 firewall mangle");
14155
14206
  async function updateMangleRule2(a, ctx) {
14156
14207
  ctx.info(`Updating IPv6 firewall mangle rule: rule_id=${a.rule_id}`);
14157
14208
  const updates = [];
@@ -14227,11 +14278,14 @@ async function updateMangleRule2(a, ctx) {
14227
14278
  }
14228
14279
  if (updates.length === 0)
14229
14280
  return "No updates specified.";
14230
- const cmd = `/ipv6 firewall mangle set ${a.rule_id} ${updates.join(" ")}`;
14281
+ const id = await resolveRuleId3(a.rule_id, ctx);
14282
+ if (!id)
14283
+ return `IPv6 mangle rule '${a.rule_id}' not found.`;
14284
+ const cmd = `/ipv6 firewall mangle set ${id} ${updates.join(" ")}`;
14231
14285
  const result = await executeMikrotikCommand(cmd, ctx);
14232
14286
  if (looksLikeError(result))
14233
14287
  return `Failed to update IPv6 firewall mangle rule: ${result}`;
14234
- const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${a.rule_id}`, ctx);
14288
+ const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${id}`, ctx);
14235
14289
  return `IPv6 firewall mangle rule updated successfully:
14236
14290
 
14237
14291
  ${details}`;
@@ -14390,8 +14444,11 @@ ${result}`;
14390
14444
  },
14391
14445
  async handler(a, ctx) {
14392
14446
  ctx.info(`Getting IPv6 firewall mangle rule details: rule_id=${a.rule_id}`);
14393
- const result = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${a.rule_id}`, ctx);
14394
- return isEmpty(result) ? `IPv6 firewall mangle rule with ID '${a.rule_id}' not found.` : `IPV6 FIREWALL MANGLE RULE DETAILS:
14447
+ const id = await resolveRuleId3(a.rule_id, ctx);
14448
+ if (!id)
14449
+ return `IPv6 mangle rule '${a.rule_id}' not found.`;
14450
+ const result = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${id}`, ctx);
14451
+ return isEmpty(result) ? `IPv6 mangle rule '${a.rule_id}' not found.` : `IPV6 FIREWALL MANGLE RULE DETAILS:
14395
14452
 
14396
14453
  ${result}`;
14397
14454
  }
@@ -14473,13 +14530,13 @@ ${result}`;
14473
14530
  inputSchema: { rule_id: z46.string() },
14474
14531
  async handler(a, ctx) {
14475
14532
  ctx.info(`Removing IPv6 firewall mangle rule: rule_id=${a.rule_id}`);
14476
- const count = await executeMikrotikCommand(`/ipv6 firewall mangle print count-only where .id=${a.rule_id}`, ctx);
14477
- if (count.trim() === "0")
14478
- return `IPv6 firewall mangle rule with ID '${a.rule_id}' not found.`;
14479
- const result = await executeMikrotikCommand(`/ipv6 firewall mangle remove ${a.rule_id}`, ctx);
14533
+ const id = await resolveRuleId3(a.rule_id, ctx);
14534
+ if (!id)
14535
+ return `IPv6 mangle rule '${a.rule_id}' not found.`;
14536
+ const result = await executeMikrotikCommand(`/ipv6 firewall mangle remove ${id}`, ctx);
14480
14537
  if (looksLikeError(result))
14481
14538
  return `Failed to remove IPv6 firewall mangle rule: ${result}`;
14482
- return `IPv6 firewall mangle rule with ID '${a.rule_id}' removed successfully.`;
14539
+ return `IPv6 mangle rule '${a.rule_id}' (${id}) removed successfully.`;
14483
14540
  }
14484
14541
  }),
14485
14542
  defineTool({
@@ -14493,13 +14550,13 @@ ${result}`;
14493
14550
  },
14494
14551
  async handler(a, ctx) {
14495
14552
  ctx.info(`Moving IPv6 firewall mangle rule: rule_id=${a.rule_id} to position ${a.destination}`);
14496
- const count = await executeMikrotikCommand(`/ipv6 firewall mangle print count-only where .id=${a.rule_id}`, ctx);
14497
- if (count.trim() === "0")
14498
- return `IPv6 firewall mangle rule with ID '${a.rule_id}' not found.`;
14499
- const result = await executeMikrotikCommand(`/ipv6 firewall mangle move ${a.rule_id} destination=${a.destination}`, ctx);
14553
+ const id = await resolveRuleId3(a.rule_id, ctx);
14554
+ if (!id)
14555
+ return `IPv6 mangle rule '${a.rule_id}' not found.`;
14556
+ const result = await executeMikrotikCommand(`/ipv6 firewall mangle move ${id} destination=${a.destination}`, ctx);
14500
14557
  if (looksLikeError(result))
14501
14558
  return `Failed to move IPv6 firewall mangle rule: ${result}`;
14502
- return `IPv6 firewall mangle rule with ID '${a.rule_id}' moved to position ${a.destination}.`;
14559
+ return `IPv6 mangle rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
14503
14560
  }
14504
14561
  }),
14505
14562
  defineTool({
@@ -14527,6 +14584,7 @@ ${result}`;
14527
14584
  // src/tools/ipv6-firewall-raw.ts
14528
14585
  import { z as z47 } from "zod";
14529
14586
  var isDigits7 = (s) => /^\d+$/.test(s);
14587
+ var resolveRuleId4 = ruleResolver("/ipv6 firewall raw");
14530
14588
  async function updateRawRule(a, ctx) {
14531
14589
  ctx.info(`Updating IPv6 firewall raw rule: rule_id=${a.rule_id}`);
14532
14590
  const updates = [];
@@ -14572,11 +14630,14 @@ async function updateRawRule(a, ctx) {
14572
14630
  }
14573
14631
  if (updates.length === 0)
14574
14632
  return "No updates specified.";
14575
- const cmd = `/ipv6 firewall raw set ${a.rule_id} ${updates.join(" ")}`;
14633
+ const id = await resolveRuleId4(a.rule_id, ctx);
14634
+ if (!id)
14635
+ return `IPv6 raw rule '${a.rule_id}' not found.`;
14636
+ const cmd = `/ipv6 firewall raw set ${id} ${updates.join(" ")}`;
14576
14637
  const result = await executeMikrotikCommand(cmd, ctx);
14577
14638
  if (looksLikeError(result))
14578
14639
  return `Failed to update IPv6 firewall raw rule: ${result}`;
14579
- const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${a.rule_id}`, ctx);
14640
+ const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${id}`, ctx);
14580
14641
  return `IPv6 firewall raw rule updated successfully:
14581
14642
 
14582
14643
  ${details}`;
@@ -14691,7 +14752,10 @@ ${result}`;
14691
14752
  },
14692
14753
  async handler(a, ctx) {
14693
14754
  ctx.info(`Getting IPv6 firewall raw rule details: rule_id=${a.rule_id}`);
14694
- const result = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${a.rule_id}`, ctx);
14755
+ const id = await resolveRuleId4(a.rule_id, ctx);
14756
+ if (!id)
14757
+ return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
14758
+ const result = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${id}`, ctx);
14695
14759
  return isEmpty(result) ? `IPv6 firewall raw rule with ID '${a.rule_id}' not found.` : `IPV6 FIREWALL RAW RULE DETAILS:
14696
14760
 
14697
14761
  ${result}`;
@@ -14745,13 +14809,13 @@ ${result}`;
14745
14809
  inputSchema: { rule_id: z47.string() },
14746
14810
  async handler(a, ctx) {
14747
14811
  ctx.info(`Removing IPv6 firewall raw rule: rule_id=${a.rule_id}`);
14748
- const count = await executeMikrotikCommand(`/ipv6 firewall raw print count-only where .id=${a.rule_id}`, ctx);
14749
- if (count.trim() === "0")
14812
+ const id = await resolveRuleId4(a.rule_id, ctx);
14813
+ if (!id)
14750
14814
  return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
14751
- const result = await executeMikrotikCommand(`/ipv6 firewall raw remove ${a.rule_id}`, ctx);
14815
+ const result = await executeMikrotikCommand(`/ipv6 firewall raw remove ${id}`, ctx);
14752
14816
  if (looksLikeError(result))
14753
14817
  return `Failed to remove IPv6 firewall raw rule: ${result}`;
14754
- return `IPv6 firewall raw rule with ID '${a.rule_id}' removed successfully.`;
14818
+ return `IPv6 firewall raw rule '${a.rule_id}' (${id}) removed successfully.`;
14755
14819
  }
14756
14820
  }),
14757
14821
  defineTool({
@@ -14765,13 +14829,13 @@ ${result}`;
14765
14829
  },
14766
14830
  async handler(a, ctx) {
14767
14831
  ctx.info(`Moving IPv6 firewall raw rule: rule_id=${a.rule_id} to position ${a.destination}`);
14768
- const count = await executeMikrotikCommand(`/ipv6 firewall raw print count-only where .id=${a.rule_id}`, ctx);
14769
- if (count.trim() === "0")
14832
+ const id = await resolveRuleId4(a.rule_id, ctx);
14833
+ if (!id)
14770
14834
  return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
14771
- const result = await executeMikrotikCommand(`/ipv6 firewall raw move ${a.rule_id} destination=${a.destination}`, ctx);
14835
+ const result = await executeMikrotikCommand(`/ipv6 firewall raw move ${id} destination=${a.destination}`, ctx);
14772
14836
  if (looksLikeError(result))
14773
14837
  return `Failed to move IPv6 firewall raw rule: ${result}`;
14774
- return `IPv6 firewall raw rule with ID '${a.rule_id}' moved to position ${a.destination}.`;
14838
+ return `IPv6 firewall raw rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
14775
14839
  }
14776
14840
  }),
14777
14841
  defineTool({
@@ -22133,7 +22197,7 @@ ${result}`;
22133
22197
  ${v6Result}`;
22134
22198
  }
22135
22199
  const where = [`dst-address in ${a.destination}`, "active=yes"];
22136
- if (table)
22200
+ if (table && table !== "main")
22137
22201
  where.push(`routing-table=${quoteValue(table)}`);
22138
22202
  const v7Cmd = `/ip route print detail where ${where.join(" ")}`;
22139
22203
  const v7Result = await executeMikrotikCommand(v7Cmd, ctx);
@@ -25027,6 +25091,7 @@ var serverPulseTools = [
25027
25091
  defineTool({
25028
25092
  name: "check_server_pulse",
25029
25093
  title: "Server Pulse & Update Check",
25094
+ noDevice: true,
25030
25095
  annotations: READ,
25031
25096
  description: "Check the MCP server's own heartbeat: running version, whether a newer release " + "is available, release notes for the latest version, upgrade commands, and server " + "vitals (tool count, uptime). This is server self-awareness \u2014 no RouterOS device " + "is contacted. Call this when the user asks about the MCP server version, updates, " + "what's new, or 'is my server up to date'. Returns rich release notes from GitHub " + "so you can summarize what changed.",
25032
25097
  inputSchema: {
@@ -25056,8 +25121,9 @@ var serverPulseTools = [
25056
25121
  }
25057
25122
  sections.push("", `Release: ${result.release.url}`);
25058
25123
  }
25059
- if (includeNotes && result.release.body && result.release.isNewer) {
25060
- sections.push("", `WHAT'S NEW IN v${result.release.version}`, sep, result.release.body);
25124
+ if (includeNotes && result.release.body) {
25125
+ const notesHeader = result.release.isNewer ? `WHAT'S NEW IN v${result.release.version}` : `RELEASE NOTES \u2014 v${result.release.version}`;
25126
+ sections.push("", notesHeader, sep, result.release.body);
25061
25127
  }
25062
25128
  } else {
25063
25129
  sections.push("", "UPDATE STATUS: UNKNOWN", sep, `Could not check for updates${result.error ? `: ${result.error}` : "."}`, `Current version: v${VERSION}`, result.fromCache ? "(showing cached data)" : "(no cached data available \u2014 check network connectivity)");
@@ -4,7 +4,7 @@ import {
4
4
  allToolModules,
5
5
  moduleCatalog,
6
6
  selectToolModules
7
- } from "./library-84jwmz7a.js";
7
+ } from "./library-7j22z9a8.js";
8
8
  export {
9
9
  selectToolModules,
10
10
  moduleCatalog,